OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 155 → Rev 156

/trunk/OpenConcerto/src/META-INF/services/java.nio.file.spi.FileTypeDetector
New file
0,0 → 1,0
ilm.utils.mime.FreeDesktopMimeDetector
/trunk/OpenConcerto/src/org/jopenchart/DataModel1D.java
4,7 → 4,7
import java.util.List;
 
public class DataModel1D extends DataModel {
private final List<Number> l = new ArrayList<Number>();
private final List<Number> l = new ArrayList<>();
 
public DataModel1D() {
 
12,8 → 12,7
 
public DataModel1D(Number[] data) {
for (int i = 0; i < data.length; i++) {
Number number = data[i];
l.add(number);
l.add(data[i]);
}
}
 
21,15 → 20,15
this.addAll(list);
}
 
public void addAll(List<Number> data) {
public synchronized void addAll(List<Number> data) {
l.addAll(data);
}
 
public int getSize() {
public synchronized int getSize() {
return l.size();
}
 
public void setValueAt(int index, Number value) {
public synchronized void setValueAt(int index, Number value) {
ensureCapacity(index);
l.set(index, value);
}
40,37 → 39,34
}
}
 
public Number getValueAt(int index) {
public synchronized Number getValueAt(int index) {
ensureCapacity(index);
return l.get(index);
}
 
public Number getMaxValue() {
public synchronized Number getMaxValue() {
Number max = 0;
for (Number b : this.l) {
if (max == null) {
if (b != null && b.doubleValue() > max.doubleValue()) {
max = b;
} else if (b != null && b.doubleValue() > max.doubleValue()) {
max = b;
}
}
return max;
}
 
public Number getMinValue() {
public synchronized Number getMinValue() {
Number min = 0;
for (Number b : this.l) {
if (min == null) {
if (b != null && b.doubleValue() < min.doubleValue()) {
min = b;
} else if (b != null && b.doubleValue() < min.doubleValue()) {
min = b;
}
}
return min;
}
 
public void clear() {
for (int i = 0; i < this.getSize(); i++) {
public synchronized void clear() {
final int size = l.size();
for (int i = 0; i < size; i++) {
this.setValueAt(i, null);
}
}
/trunk/OpenConcerto/src/org/jopenchart/DataModel2D.java
7,10 → 7,10
public class DataModel2D extends DataModel {
 
private String[][] data;
private int rowCount;
private int colCount;
private List<String> rowLabels = new ArrayList<String>();
private List<String> colLabels = new ArrayList<String>();
private final int rowCount;
private final int colCount;
private List<String> rowLabels = new ArrayList<>();
private List<String> colLabels = new ArrayList<>();
 
public DataModel2D(int row, int col) {
this.rowCount = row;
27,24 → 27,24
this.rowLabels.add(String.valueOf((char) ('A' + i)));
}
for (int i = 0; i < col; i++) {
this.colLabels.add(String.valueOf(((1 + i))));
this.colLabels.add(String.valueOf(1 + i));
}
 
}
 
public String getValue(int row, int col) {
public synchronized String getValue(int row, int col) {
return data[row][col];
}
 
public void setValue(String value, int row, int col) {
public synchronized void setValue(String value, int row, int col) {
data[row][col] = value;
}
 
public String getColumnLabel(int col) {
public synchronized String getColumnLabel(int col) {
return this.colLabels.get(col);
}
 
public String getRowLabel(int row) {
public synchronized String getRowLabel(int row) {
return this.rowLabels.get(row);
}
 
/trunk/OpenConcerto/src/org/openconcerto/map/model/Ville.java
15,7 → 15,6
 
import org.openconcerto.map.ui.MapViewerPanel;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.StringUtils;
 
import java.awt.Color;
import java.awt.Polygon;
40,19 → 39,17
 
public class Ville {
// TODO: switch from Lambert to Mercator (see jmapprojlib SF project)
private static Map<String, Ville> map = new HashMap<String, Ville>();
private static Map<String, Ville> map = new HashMap<>();
private static DatabaseAccessor accessor;
private final static List<Ville> villes = new ArrayList<Ville>(39000);
private final static List<String> villesNames = new ArrayList<String>(39000);
private final static List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
private static final List<Ville> villes = new ArrayList<>(39000);
private static final List<String> villesNames = new ArrayList<>(39000);
private static final List<PropertyChangeListener> listeners = new ArrayList<>();
private static Thread init = null;
private static boolean loaded = false;
private int nbMatch = 0;
 
public synchronized static void init(final DatabaseAccessor d, final boolean loadFromFile) {
 
public static synchronized void init(final DatabaseAccessor d, final boolean loadFromFile) {
await();
 
accessor = d;
init = new Thread(new Runnable() {
@Override
132,20 → 129,6
bufReader.close();
}
 
private static final void parseLine(final String line) {
 
final List<String> strs = StringUtils.fastSplit(line, ';');
final Ville v = new Ville(strs.get(3), parsePositiveLong(strs.get(4)), parsePositiveLong(strs.get(8)), parsePositiveLong(strs.get(9)), strs.get(2));
if (strs.size() > 10) {
v.setMinimumZoom(parsePositiveLong(strs.get(10)));
}
if (v.xLambert > 0) {
addVilleSilently(v);
// System.out.println(v);
}
 
}
 
private static synchronized void addVilleSilently(final Ville v) {
villes.add(v);
final String villeEtCode = v.getVilleEtCode();
217,7 → 200,7
 
public static synchronized List<Ville> getVillesContaining(String string) {
string = string.trim().toLowerCase();
List<Ville> list = new ArrayList<Ville>();
List<Ville> list = new ArrayList<>();
final List<Ville> l = getVilles();
final int size = l.size();
for (int i = 0; i < size; i++) {
235,7 → 218,7
}
 
List<Ville> l = getVillesFromCode(codepostal);
if (l.size() == 0) {
if (l.isEmpty()) {
return null;
}
if (l.size() == 1) {
255,8 → 238,7
}
 
private static List<Ville> getVillesFromCode(String cp) {
 
List<Ville> list = new ArrayList<Ville>();
final List<Ville> list = new ArrayList<>();
final List<Ville> l = getVilles();
final int size = l.size();
for (int i = 0; i < size; i++) {
275,13 → 257,13
* @return the cities, or <code>null</code> si l'integralité des villes ou si la selection
* comporte moins de 3 points.
*/
public synchronized static List<Ville> getVilleIn(final MapPointSelection sel) {
ArrayList<Ville> r = null;
public static synchronized List<Ville> getVilleIn(final MapPointSelection sel) {
if (sel == null) {
return null;
}
ArrayList<Ville> r = null;
if (sel.size() > 2) {
r = new ArrayList<Ville>();
r = new ArrayList<>();
final Polygon p = new Polygon();
 
for (int i = 0; i < sel.size(); i++) {
315,7 → 297,6
if (!p.contains(x, y))
continue;
r.add(v);
// System.out.println("match:" + v);
}
}
return r;
323,48 → 304,40
 
public static long parsePositiveLong(String str) {
str = str.trim();
 
long value = 0;
final int stop = str.length();
 
for (int i = 0; i < stop; i++) {
final char c = str.charAt(i);
 
if (c == '.') {
break;
}
 
value *= 10;
value += (c - '0');
 
}
 
// System.out.println("str" + str + "->" + value);
return value;
}
 
public static synchronized long getMinXLambert() {
final List<Ville> l = getVilles();
if (l.size() > 0) {
if (l.isEmpty()) {
return 0;
}
long min = l.get(0).xLambert;
for (int i = 0; i < l.size(); i++) {
final Ville v = l.get(i);
 
if (v.xLambert < min) {
min = v.xLambert;
}
 
}
return min;
} else {
return 0;
}
 
}
 
static synchronized long getMaxXLambert() {
final List<Ville> l = getVilles();
if (l.size() > 0) {
if (l.isEmpty()) {
return 0;
}
long max = l.get(0).xLambert;
for (int i = 0; i < l.size(); i++) {
final Ville v = l.get(i);
373,13 → 346,13
}
}
return max;
} else {
return 0;
}
}
 
public static synchronized long getMinYLambert() {
final List<Ville> l = getVilles();
if (l.isEmpty()) {
return 0;
}
long min = l.get(0).yLambert;
for (int i = 0; i < l.size(); i++) {
final Ville v = l.get(i);
392,6 → 365,9
 
static synchronized long getMaxYLambert() {
final List<Ville> l = getVilles();
if (l.isEmpty()) {
return 0;
}
long max = l.get(0).yLambert;
for (int i = 0; i < l.size(); i++) {
final Ville v = l.get(i);
418,9 → 394,6
 
long population;
 
//
private long minimumZoom;
 
private Color color = null;
 
public String getCodepostal() {
468,15 → 441,6
this.color = color;
}
 
private void setMinimumZoom(final long l) {
this.minimumZoom = l;
 
}
 
public long getMinimumZoom() {
return this.minimumZoom;
}
 
public void setNbMatch(int nb) {
this.nbMatch = nb;
}
495,8 → 459,6
 
Ville.parseFile();
 
// Thread.sleep(10*1000);
 
final long t2 = System.nanoTime();
System.out.println("Parsing: " + (t2 - t1) / 1000000 + " ms");
System.out.println("MinXLambert:" + getMinXLambert() + ",MinYLambert" + getMinYLambert());
/trunk/OpenConcerto/src/org/openconcerto/map/model/villes.txt
176252,4 → 176252,9
75251
664756
6857877
94100
94100
Sainte-Marie-du-Lac-Nuisement
250
834134
6837080
51290
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Table.java
427,8 → 427,13
* @see #getImmutableCellAt(int, int)
*/
public final MutableCell<D> getCellAt(int x, int y) {
return this.getMutableRow(y).getMutableCellAt(x);
final Row<D> r = this.getMutableRow(y);
try {
return r.getMutableCellAt(x);
} catch (Exception e) {
throw new IllegalArgumentException("Couldn't get mutable cell at " + getAddress(x, y), e);
}
}
 
public final MutableCell<D> getCellAt(String ref) {
return this.getCellAt(resolveHint(ref));
1391,8 → 1396,12
* @return the string address, e.g. "AA34".
*/
static final String getAddress(Point p) {
if (p.x < 0 || p.y < 0)
throw new IllegalArgumentException("negative coordinates : " + p);
return toStr(p.x) + (p.y + 1);
return getAddress(p.x, p.y);
}
 
static final String getAddress(final int x, final int y) {
if (x < 0 || y < 0)
throw new IllegalArgumentException("negative coordinates : " + x + ":" + y);
return toStr(x) + (y + 1);
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Range.java
16,7 → 16,11
import org.openconcerto.utils.CompareUtils;
 
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
/**
* A cell range.
25,6 → 29,14
*/
public final class Range {
 
// added illegal characters to unquoted form from LibreOffice UI, especially ':' to avoid
// mistakenly using it in the table name instead of as a separator
private static final String tableNamePattern = "\\$?([^\\Q. '[]*?:/\\\\E]+|'([^']|'')+')";
// added parens to capture cell addresses
// \1 is sheet name, \4 cell address, \6 second sheet name, \9 second cell address
private static final Pattern cellRangePattern = java.util.regex.Pattern.compile("^(" + tableNamePattern + ")?\\.(\\$?[A-Z]+\\$?[0-9]+)(:(" + tableNamePattern + ")?\\.(\\$?[A-Z]+\\$?[0-9]+))?");
private static final Pattern LIST_SEPARATOR_PATTERN = Pattern.compile("^\\s+");
 
/**
* Parse a range.
*
32,9 → 44,13
* @return the parsed range.
*/
static public final Range parse(String range) {
final Matcher m = SpreadSheet.cellRangePattern.matcher(range);
final Matcher m = cellRangePattern.matcher(range);
if (!m.matches())
throw new IllegalStateException(range + " is not a valid range address");
return parse(m);
}
 
static private final Range parse(final Matcher m) {
final String sheet1 = SpreadSheet.parseSheetName(m.group(1));
final String sheet2 = SpreadSheet.parseSheetName(m.group(6));
 
45,6 → 61,37
return new Range(sheet1, start, sheet2, end);
}
 
// 9.2.5 Cell Range Address List, used by "table:print-ranges" for example.
// e.g. "Feuille1.A1:Feuille1.L65 Feuille1.A67:Feuille1.L107"
static public final List<Range> parseList(String ranges) {
if (ranges.isEmpty())
return Collections.emptyList();
 
final Matcher m = cellRangePattern.matcher(ranges);
m.useAnchoringBounds(true);
if (!m.find())
throw new IllegalStateException(ranges + " is not a valid range address list");
final Range first = parse(m);
final int length = ranges.length();
if (length == m.end())
return Collections.singletonList(first);
 
final List<Range> res = new ArrayList<>();
res.add(first);
final Matcher sepMatcher = LIST_SEPARATOR_PATTERN.matcher(ranges);
sepMatcher.useAnchoringBounds(true);
while (m.end() != length) {
sepMatcher.region(m.end(), length);
if (!sepMatcher.find())
throw new IllegalStateException("Couldn't find separator at " + sepMatcher.regionStart() + " in " + ranges);
m.region(sepMatcher.end(), length);
if (!m.find())
throw new IllegalStateException("Couldn't find range address at " + m.regionStart() + " in " + ranges);
res.add(parse(m));
}
return res;
}
 
private final String sheet1, sheet2;
private final Point start, end;
 
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Row.java
141,7 → 141,7
protected final Cell<D> getValidCellAt(int col) {
final Cell<D> c = this.getCellAt(col);
if (!c.isValid())
throw new IllegalArgumentException("invalid cell " + c);
throw new IllegalArgumentException("invalid cell at column " + col + " (in " + this + ") : " + c);
return c;
}
 
208,4 → 208,11
assert this.getCellCount() == this.getSheet().getColumnCount();
}
 
@Override
public String toString() {
final int firstY = this.getY();
final int lastY = this.getLastY();
final String suffix = firstY == lastY ? "" : " through " + lastY;
return this.getClass().getSimpleName() + " with " + this.getCellCount() + " cell(s) at index " + firstY + suffix;
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/SpreadSheet.java
145,12 → 145,6
// \1 is sheet name, \4 cell address
static final Pattern cellPattern = Pattern.compile("(\\$?([^\\. ']+|'([^']|'')+'))?\\.(" + minCell + ")");
static final Pattern minCellPattern = Pattern.compile(minCell);
// added illegal characters to unquoted form from LibreOffice UI, especially ':' to avoid
// mistakenly using it in the table name instead of as a separator
private static String tableNamePattern = "\\$?([^\\Q. '[]*?:/\\\\E]+|'([^']|'')+')";
// added parens to capture cell addresses
// \1 is sheet name, \4 cell address, \6 second sheet name, \9 second cell address
static final Pattern cellRangePattern = java.util.regex.Pattern.compile("(" + tableNamePattern + ")?\\.(\\$?[A-Z]+\\$?[0-9]+)(:(" + tableNamePattern + ")?\\.(\\$?[A-Z]+\\$?[0-9]+))?");
 
// see 9.2.1 of OpenDocument-v1.2-cs01-part1
static final Pattern tableNameQuoteQuotePattern = Pattern.compile("''", Pattern.LITERAL);
/trunk/OpenConcerto/src/org/openconcerto/ui/preferences/EmailProps.java
13,6 → 13,11
package org.openconcerto.ui.preferences;
 
import org.openconcerto.utils.FileUtils;
 
import java.io.File;
import java.io.IOException;
 
public class EmailProps extends AbstractProps {
public static final int DEFAULT = 0;
public static final int THUNDERBIRD = 1;
19,6 → 24,8
public static final int OUTLOOK = 2;
protected static EmailProps instance;
private String propsFileName = null;
private static String DEFAULT_FILE = "./Configuration/Email.properties";
private String defaultPropsFileName = DEFAULT_FILE;
 
public synchronized static EmailProps getInstance() {
if (instance == null) {
30,14 → 37,36
@Override
protected String getPropsFileName() {
if (this.propsFileName == null) {
return "./Configuration/Email.properties";
return getDefaultPropsFileName();
} else {
return this.propsFileName;
}
}
 
public void setDefaultPropsFileName(String defaultPropsFileName) {
this.defaultPropsFileName = defaultPropsFileName;
}
 
protected String getDefaultPropsFileName() {
if (this.defaultPropsFileName == null) {
return DEFAULT_FILE;
} else {
return defaultPropsFileName;
}
}
 
public void setPropsFileName(String propsFileName) {
this.propsFileName = propsFileName;
final File file = new File(getPropsFileName());
final File fileDefault = new File(getDefaultPropsFileName());
if (!file.exists() && fileDefault.exists()) {
try {
FileUtils.copyFile(fileDefault, file);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
load();
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightControler.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIStrippedImageLine.java
59,16 → 59,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIStrippedImageLine(json);
}
};
}
 
@Override
public String getHTML() {
StringBuilder b = new StringBuilder();
for (LightUIImage image : this.images) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIPasswordField.java
New file
0,0 → 1,57
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUIPasswordField extends LightUserControl {
 
public LightUIPasswordField() {
// Serialization
}
 
public LightUIPasswordField(final String id) {
super(id);
this.setType(TYPE_PASSWORD_FIELD);
this.setValueType(LightUIElement.VALUE_TYPE_STRING);
}
 
public LightUIPasswordField(final JSONObject json) {
super(json);
}
 
public LightUIPasswordField(final LightUIPasswordField text) {
super(text);
}
 
@Override
public Object getValueForContext() {
if (this.getValue() == null || this.getValue().trim().isEmpty()) {
return null;
}
return this.getValue();
}
 
@Override
public void _setValueFromContext(final Object value) {
final String strValue = JSONConverter.getObjectFromJSON(value, String.class);
if (strValue != null && !strValue.trim().isEmpty()) {
this.setValue(strValue);
} else {
this.setValue(null);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/JSONToLightUIConvertorManager.java
20,7 → 20,7
import net.minidev.json.JSONObject;
 
public class JSONToLightUIConvertorManager {
HashMap<String, JSONToLightUIConvertor> map = new HashMap<String, JSONToLightUIConvertor>();
HashMap<String, JSONToLightUIConvertor> map = new HashMap<>();
 
private static final JSONToLightUIConvertorManager instance = new JSONToLightUIConvertorManager();
 
33,15 → 33,206
}
 
public LightUIElement createUIElementFromJSON(final JSONObject jsonElement) {
final Integer elementType = (Integer) JSONConverter.getParameterFromJSON(jsonElement, "type", Integer.class, null);
if (elementType == null) {
throw new IllegalArgumentException("LightUIElement must contains attribute 'type'");
final Integer type = (Integer) JSONConverter.getParameterFromJSON(jsonElement, "type", Integer.class, null);
switch (type) {
case LightUIElement.TYPE_LABEL: {
final LightUILabel l = new LightUILabel();
l.fromJSON(jsonElement);
return l;
}
final String className = (String) JSONConverter.getParameterFromJSON(jsonElement, "class-name", String.class);
if (className == null) {
throw new IllegalArgumentException("class-name must be set");
case LightUIElement.TYPE_TEXT_FIELD: {
final LightUITextField l = new LightUITextField();
l.fromJSON(jsonElement);
return l;
 
}
final JSONToLightUIConvertor convertor = this.map.get(className);
return convertor.convert(jsonElement);
case LightUIElement.TYPE_DATE: {
final LightUIDate l = new LightUIDate();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_COMBOBOX: {
final LightUIComboBox l = new LightUIComboBox();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_TABLE: {
final LightUITable l = new LightUITable();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_CHECKBOX: {
final LightUICheckBox l = new LightUICheckBox();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_TABBED_UI: {
final LightUITabbed l = new LightUITabbed();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_COMBOBOX_ELEMENT: {
// TODO : WTF
throw new IllegalAccessError("WTF");
}
case LightUIElement.TYPE_PANEL: {
final LightUIPanel l = new LightUIPanel();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_TREE: {
// TODO : TYPE_TREE
throw new IllegalAccessError("TODO");
 
}
case LightUIElement.TYPE_TEXT: {
final LightUIText l = new LightUIText(jsonElement);
return l;
 
}
case LightUIElement.TYPE_LIST: {
final LightUIList l = new LightUIList(jsonElement);
return l;
 
}
case LightUIElement.TYPE_DROPDOWN_BUTTON: {
final LightUIDropDownButton l = new LightUIDropDownButton(jsonElement);
return l;
 
}
case LightUIElement.TYPE_FRAME: {
final LightUIFrame l = new LightUIFrame();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_IMAGE: {
final LightUIImage l = new LightUIImage(jsonElement);
return l;
}
case LightUIElement.TYPE_FILE_UPLOAD_WITH_SELECTION: {
final LightUIFileUpload l = new LightUIFileUpload();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_PANEL_LINE: {
final LightUILine l = new LightUILine();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_TAB_ELEMENT: {
final LightUITab l = new LightUITab(jsonElement);
return l;
 
}
case LightUIElement.TYPE_SLIDER: {
final LightUISlider l = new LightUISlider();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_PICTURE_UPLOAD: {
final LightUIPictureUpload l = new LightUIPictureUpload();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_BUTTON: {
final LightUIButton l = new LightUIButton();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_BUTTON_WITH_CONTEXT: {
final LightUIButtonWithContext l = new LightUIButtonWithContext(jsonElement);
return l;
 
}
case LightUIElement.TYPE_BUTTON_CANCEL: {
final LightUIButton button = new LightUIButton();
button.fromJSON(jsonElement);
return button;
}
case LightUIElement.TYPE_BUTTON_UNMANAGED: {
final LightUIButtonUnmanaged l = new LightUIButtonUnmanaged();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_BUTTON_WITH_SELECTION_CONTEXT: {
final LightUIButtonWithSelectionContext l = new LightUIButtonWithSelectionContext(jsonElement);
return l;
}
case LightUIElement.TYPE_BUTTON_LINK: {
final LightUIButtonLink l = new LightUIButtonLink();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_RAW_HTML: {
final LightUIRawHMTL l = new LightUIRawHMTL(jsonElement);
return l;
 
}
case LightUIElement.TYPE_TEXT_AREA: {
final LightUITextArea l = new LightUITextArea(jsonElement);
return l;
 
}
case LightUIElement.TYPE_FILE_UPLOAD: {
final LightUIFileUpload l = new LightUIFileUpload();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_LIST_ROW: {
final LightUIListRow l = new LightUIListRow(jsonElement);
return l;
 
}
case LightUIElement.TYPE_BADGE: {
final LightUIBadge l = new LightUIBadge(jsonElement);
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_AUTOCOMPLETE_COMBOBOX: {
final LightUIAutoCompleteComboBox l = new LightUIAutoCompleteComboBox();
l.fromJSON(jsonElement);
return l;
}
case LightUIElement.TYPE_COLOR_PICKER: {
final LightUIColorPicker l = new LightUIColorPicker(jsonElement);
return l;
 
}
case LightUIElement.TYPE_HOUR_EDITOR: {
final LightUIHourEditor l = new LightUIHourEditor(jsonElement);
return l;
 
}
case LightUIElement.TYPE_RADIO_BUTTONS: {
final LightUIRadioButtons l = new LightUIRadioButtons();
l.fromJSON(jsonElement);
return l;
 
}
case LightUIElement.TYPE_PASSWORD_FIELD: {
final LightUIPasswordField l = new LightUIPasswordField();
l.fromJSON(jsonElement);
return l;
 
}
 
}
throw new IllegalArgumentException("unsupported type " + type);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TableSpec.java
16,9 → 16,14
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
import net.minidev.json.JSONObject;
 
public class TableSpec implements Transferable {
public class TableSpec implements Transferable, Externalizable {
private String id;
private ColumnsSpec columns;
private TableContent content;
26,6 → 31,10
private SearchSpec search;
private Boolean variableColumnsCount = false;
 
public TableSpec() {
// Serialization
}
 
public TableSpec(final String tableId, final RowSelectionSpec selection, final ColumnsSpec columns) {
this.id = tableId + ".spec";
if (selection == null) {
142,4 → 151,32
 
this.variableColumnsCount = (Boolean) JSONConverter.getParameterFromJSON(json, "variable-columns-count", Boolean.class);
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(id);
this.columns.writeExternal(out);
this.content.writeExternal(out);
 
this.selection.writeExternal(out);
this.search.writeExternal(out);
 
out.writeBoolean(this.variableColumnsCount);
 
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readUTF();
this.columns = new ColumnsSpec();
this.columns.readExternal(in);
this.content = new TableContent();
this.content.readExternal(in);
this.selection = new RowSelectionSpec();
this.selection.readExternal(in);
this.search = new SearchSpec();
this.search.readExternal(in);
this.variableColumnsCount = in.readBoolean();
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIRadioButtons.java
13,6 → 13,9
package org.openconcerto.ui.light;
 
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
 
22,6 → 25,10
public class LightUIRadioButtons extends LightUserControl {
private final List<String> choices = new ArrayList<>();
 
public LightUIRadioButtons() {
// Serialization
}
 
// Init from json constructor
public LightUIRadioButtons(final JSONObject json) {
super(json);
86,13 → 93,22
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeInt(this.choices.size());
for (String c : this.choices) {
out.writeUTF(c);
}
}
 
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIRadioButtons(json);
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
int size = in.readInt();
this.choices.clear();
for (int i = 0; i < size; i++) {
this.choices.add(in.readUTF());
}
};
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUILabel.java
16,6 → 16,11
import net.minidev.json.JSONObject;
 
public class LightUILabel extends LightUIElement {
 
public LightUILabel() {
// Serialization
}
 
// Init from json constructor
public LightUILabel(final JSONObject json) {
super(json);
45,14 → 50,4
this.setFontBold(isBold);
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUILabel(json);
}
};
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITabbed.java
17,7 → 17,11
 
import net.minidev.json.JSONObject;
 
public abstract class LightUITabbed extends LightUserControlContainer {
public class LightUITabbed extends LightUserControlContainer {
 
public LightUITabbed() {
}
 
// Init from json constructor
public LightUITabbed(final JSONObject json) {
super(json);
42,7 → 46,8
* @param tabId: id of tab which you want to load. The tab id must match with a tab contained in
* childs
*/
public abstract void loadTab(final String tabId);
public void loadTab(final String tabId) {
}
 
@Override
public void setValue(final String value) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/RowsBulk.java
54,11 → 54,11
if (rowCount == 0) {
this.rows = Collections.emptyList();
} else {
this.rows = new ArrayList<Row>(rowCount);// colcount
this.rows = new ArrayList<>(rowCount);
int columnCount = in.readByte();
// id
for (int j = 0; j < rowCount; j++) {
Row row = new Row((Number) in.readObject(), columnCount);
Row row = new Row((Number) in.readObject());
this.rows.add(row);
}
 
82,7 → 82,7
int rowCount = this.rows.size();
out.writeInt(rowCount);
// content
if (this.rows.size() > 0) {
if (!this.rows.isEmpty()) {
// nbcols
int columnCount = this.rows.get(0).getValues().size();
out.writeByte(columnCount);
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIHourEditor.java
74,16 → 74,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIHourEditor(json);
}
};
}
 
@Override
public Object getValueForContext() {
return getIntVal();
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButtonLink.java
29,6 → 29,10
private Boolean openNewFrame = false;
private Map<String, String> urlParameters = new HashMap<String, String>();
 
public LightUIButtonLink() {
// Serialization
}
 
public LightUIButtonLink(final String id, final String filePath, final boolean openNewFrame) {
super(id);
this.setType(LightUIElement.TYPE_BUTTON_LINK);
44,6 → 48,12
super(button);
}
 
@Override
public void destroy() {
super.destroy();
urlParameters = null;
}
 
public Boolean isOpenNewFrame() {
return this.openNewFrame;
}
81,16 → 91,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIButtonLink(json);
}
};
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
 
100,7 → 100,6
if (!this.urlParameters.isEmpty()) {
json.put(URL_PARAMETER_KEY, this.urlParameters);
}
 
return json;
}
 
110,4 → 109,5
this.openNewFrame = JSONConverter.getParameterFromJSON(json, OPEN_NEW_FRAME_KEY, Boolean.class, false);
this.urlParameters = CollectionUtils.castMap(JSONConverter.getParameterFromJSON(json, URL_PARAMETER_KEY, Map.class), String.class, String.class);
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFrame.java
15,6 → 15,9
 
import org.openconcerto.utils.io.JSONConverter;
 
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
35,6 → 38,10
 
private List<LightUIFrame> childrenFrame;
 
public LightUIFrame() {
// Serialization
}
 
// Init from json constructor
public LightUIFrame(final JSONObject json) {
super(json);
50,6 → 57,22
this.setFooterPanel(frame.footerPanel);
}
 
@Override
public void destroy() {
super.destroy();
if (this.titlePanel != null) {
this.titlePanel.destroy();
}
if (this.footerPanel != null) {
this.footerPanel.destroy();
}
if (this.childrenFrame != null) {
for (LightUIFrame frame : childrenFrame) {
frame.destroy();
}
}
}
 
/**
* Creation of an instance of a frame, this one is initialized with an empty main panel
*
228,16 → 251,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIFrame(json);
}
};
}
 
@Override
public void dump(final PrintStream out, final int depth) {
out.println("------------- LightUIFrame -------------");
super.dump(out, 0);
269,7 → 282,7
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
this.active = JSONConverter.getParameterFromJSON(json, KEY_ACTIVE, Boolean.class, false);
 
this.createTitlePanel();
final JSONObject jsonTitlePanel = JSONConverter.getParameterFromJSON(json, KEY_TITLE_PANEL, JSONObject.class);
if (jsonTitlePanel != null) {
this.titlePanel.fromJSON(jsonTitlePanel);
290,4 → 303,37
}
}
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeBoolean(this.active);
// Title panel
if (this.titlePanel != null) {
out.writeBoolean(true);
out.writeObject(this.titlePanel);
} else {
out.writeBoolean(false);
}
// Footer panel
if (this.footerPanel != null) {
out.writeBoolean(true);
out.writeObject(this.footerPanel);
} else {
out.writeBoolean(false);
}
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
this.active = in.readBoolean();
if (in.readBoolean()) {
this.titlePanel = (LightUIPanel) in.readObject();
}
if (in.readBoolean()) {
this.footerPanel = (LightUIPanel) in.readObject();
}
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIPanel.java
17,6 → 17,9
import org.openconcerto.utils.io.Transferable;
 
import java.awt.Color;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
25,14 → 28,17
import net.minidev.json.JSONObject;
 
public class LightUIPanel extends LightUserControlContainer implements Transferable {
 
private static final long serialVersionUID = -3399395824294128572L;
 
private String title;
 
private Color titleColor;
private Color titleBackgroundColor;
private List<LightController> controlers = new ArrayList<>();
 
private final List<LightControler> controlers = new ArrayList<LightControler>();
public LightUIPanel() {
// Serialization
}
 
public LightUIPanel(final JSONObject json) {
super(json);
51,6 → 57,12
this.setFillHeight(true);
}
 
@Override
public void destroy() {
super.destroy();
this.controlers = null;
}
 
public final LightUILine getLastLine() {
final int childCount = this.getChildrenCount();
if (childCount == 0) {
78,11 → 90,11
this.titleBackgroundColor = c;
}
 
public void addControler(final LightControler controler) {
public void addControler(final LightController controler) {
this.controlers.add(controler);
}
 
public List<LightControler> getControlers() {
public List<LightController> getControlers() {
return this.controlers;
}
 
93,7 → 105,7
public void dumpControllers(final PrintStream out, final int depth) {
addSpacer(out, depth);
out.println("Contollers for id:" + this.getId() + " title: " + this.title);
for (LightControler controler : this.controlers) {
for (LightController controler : this.controlers) {
addSpacer(out, depth);
out.println(controler);
}
167,16 → 179,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIPanel(json);
}
};
}
 
@Override
public void dump(final PrintStream out, final int depth) {
this.addSpacer(out, depth);
out.println("------LightUIPanel-----");
254,12 → 256,45
final int controlersSize = jsonControlers.size();
for (int i = 0; i < controlersSize; i++) {
final JSONObject jsonControler = JSONConverter.getObjectFromJSON(jsonControlers.get(i), JSONObject.class);
this.controlers.add(new LightControler((JSONObject) jsonControler));
this.controlers.add(new LightController((JSONObject) jsonControler));
}
}
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
writeIfNotNull(out, this.title);
writeIfNotNull(out, this.titleColor);
writeIfNotNull(out, this.titleBackgroundColor);
out.writeByte(this.controlers.size());
for (LightController lightControler : controlers) {
lightControler.writeExternal(out);
}
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
if (in.readBoolean()) {
this.title = in.readUTF();
}
if (in.readBoolean()) {
this.titleColor = new Color(in.readInt());
}
if (in.readBoolean()) {
this.titleBackgroundColor = new Color(in.readInt());
}
this.controlers.clear();
final int size = in.readByte();
for (int i = 0; i < size; i++) {
final LightController lightControler = new LightController();
lightControler.readExternal(in);
this.controlers.add(lightControler);
}
}
 
@Override
public void setFoldable(boolean foldable) {
super.setFoldable(foldable);
if (foldable && this.title == null) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIBadge.java
29,13 → 29,4
this.setLabel(label);
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(JSONObject json) {
return new LightUIBadge(json);
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/RowSelectionSpec.java
43,12 → 43,13
}
public RowSelectionSpec(String tableId) {
this(tableId, new ArrayList<Number>());
this(tableId, new ArrayList<Number>(0));
}
 
public RowSelectionSpec(final String tableId, final List<Number> selectedIds) {
this.tableId = tableId;
this.ids = selectedIds;
this.ids = new ArrayList<>(selectedIds);
this.ids.addAll(selectedIds);
}
 
public final List<Number> getIds() {
56,15 → 57,20
}
public final void setIds(List<Number> ids) {
this.ids = ids;
this.ids.clear();
this.ids.addAll(ids);
}
 
public void clear() {
this.ids.clear();
}
 
public String getTableId() {
return this.tableId;
}
public boolean hasSelectedId(){
return this.getIds() != null && !this.getIds().isEmpty();
return !this.getIds().isEmpty();
}
 
@Override
113,7 → 119,7
public void fromJSON(JSONObject json) {
this.tableId = JSONConverter.getParameterFromJSON(json, "table-id", String.class);
final JSONArray jsonIds = JSONConverter.getParameterFromJSON(json, "ids", JSONArray.class);
this.ids = new ArrayList<Number>();
this.ids = new ArrayList<>(jsonIds.size());
for (final Object jsonId : jsonIds) {
this.ids.add(JSONConverter.getObjectFromJSON(jsonId, Number.class));
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIDate.java
18,11 → 18,18
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import net.minidev.json.JSONObject;
 
public class LightUIDate extends LightUserControl {
 
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.S";
 
public LightUIDate() {
// Serialization
}
 
public LightUIDate(final JSONObject json) {
super(json);
}
39,7 → 46,7
 
public Timestamp getValueAsDate() {
if (this.getValue() != null && !this.getValue().isEmpty()) {
SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.S");
SimpleDateFormat df2 = new SimpleDateFormat(DATE_FORMAT);
try {
return new Timestamp(df2.parse(this.getValue()).getTime());
} catch (final ParseException ex) {
49,15 → 56,10
return null;
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIDate(json);
public void setDate(Date date) {
SimpleDateFormat df2 = new SimpleDateFormat(DATE_FORMAT);
this.setValue(df2.format(date));
}
};
}
 
@Override
public Object getValueForContext() {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIPictureUpload.java
23,6 → 23,9
 
private String filePath = null;
 
public LightUIPictureUpload() {
}
 
public LightUIPictureUpload(final String id, final String sendFileUrl) {
super(id, sendFileUrl);
this.setType(TYPE_PICTURE_UPLOAD);
/trunk/OpenConcerto/src/org/openconcerto/ui/light/Row.java
30,7 → 30,7
public class Row implements Externalizable, JSONAble {
 
private Number id;
private String extendId;
private String extendId = "";// writeExternal crash if not set
private List<Object> values;
 
private Boolean fillWidth = false;
47,7 → 47,7
 
public Row(final Number id, int valueCount) {
this.id = id;
this.values = new ArrayList<Object>();
this.values = new ArrayList<>(valueCount);
if (valueCount > 0) {
for (int i = 0; i < valueCount; i++) {
this.values.add(null);
57,6 → 57,7
 
public Row(final Number id) {
this.id = id;
this.values = new ArrayList<>();
}
 
public final void setValues(List<Object> values) {
76,6 → 77,9
}
 
public final void setExtendId(final String extendId) {
if (extendId == null) {
throw new IllegalArgumentException("extendId cannot be null");
}
this.extendId = extendId;
}
 
182,6 → 186,8
objValue = JSONConverter.getObjectFromJSON(objValue, Long.class);
} else if (objValue instanceof Boolean) {
objValue = JSONConverter.getObjectFromJSON(objValue, Boolean.class);
} else if (objValue instanceof Double) {
objValue = JSONConverter.getObjectFromJSON(objValue, Double.class);
} else if (objValue != null) {
throw new IllegalArgumentException("unknow type: " + objValue.getClass().getName());
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIText.java
32,14 → 32,4
 
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIText(json);
}
};
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFileUpload.java
22,8 → 22,7
 
private String sendFileUrl;
 
public LightUIFileUpload(final JSONObject json) {
super(json);
public LightUIFileUpload() {
}
 
public LightUIFileUpload(final LightUIFileUpload file) {
38,16 → 37,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIFileUpload(json);
}
};
}
 
@Override
protected void copy(LightUIElement element) {
super.copy(element);
if (!(element instanceof LightUIFileUpload)) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControlContainer.java
16,6 → 16,11
import net.minidev.json.JSONObject;
 
public abstract class LightUserControlContainer extends LightUIContainer {
 
public LightUserControlContainer() {
// Serialization
}
 
public LightUserControlContainer(final String id) {
super(id);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUILine.java
16,9 → 16,14
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
import net.minidev.json.JSONObject;
 
public class LightUILine extends LightUIContainer implements Transferable {
public class LightUILine extends LightUIContainer implements Transferable, Externalizable {
private static final long serialVersionUID = 4132718509484530435L;
 
public static final int ALIGN_GRID = 0;
27,8 → 32,8
 
private int gridAlignment = ALIGN_GRID;
 
private Integer elementPadding;
private Integer elementMargin;
private int elementPadding = 0;
private int elementMargin = 0;
 
public LightUILine() {
// Id will be set when this line will be added into a panel, or set manually.
108,10 → 113,10
public JSONObject toJSON() {
final JSONObject result = super.toJSON();
result.put("class", "LightUILine");
if (this.elementPadding != null) {
if (this.elementPadding != 0) {
result.put("element-padding", this.elementPadding);
}
if (this.elementMargin != null) {
if (this.elementMargin != 0) {
result.put("element-margin", this.elementMargin);
}
if (this.gridAlignment != ALIGN_GRID) {
123,8 → 128,32
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
this.elementPadding = JSONConverter.getParameterFromJSON(json, "element-padding", Integer.class);
this.elementMargin = JSONConverter.getParameterFromJSON(json, "element-margin", Integer.class);
this.elementPadding = JSONConverter.getParameterFromJSON(json, "element-padding", Integer.class, 0);
this.elementMargin = JSONConverter.getParameterFromJSON(json, "element-margin", Integer.class, 0);
this.gridAlignment = JSONConverter.getParameterFromJSON(json, "grid-alignment", Integer.class, ALIGN_GRID);
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeInt(this.elementMargin);
out.writeInt(this.elementPadding);
out.writeByte(this.gridAlignment);
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
this.elementMargin = in.readInt();
this.elementPadding = in.readInt();
this.gridAlignment = in.readByte();
}
 
@Override
public void addChild(LightUIElement child) {
if (child instanceof LightUILine) {
throw new IllegalArgumentException("child is a LightUILine");
}
super.addChild(child);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/SearchSpec.java
13,19 → 13,28
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONAware;
import net.minidev.json.JSONObject;
 
public class SearchSpec implements Transferable, JSONAware {
public class SearchSpec implements Transferable, JSONAware, Externalizable {
private String tableId;
private List<SearchContent> content = new ArrayList<SearchContent>();
private List<SearchContent> content = new ArrayList<>();
 
public SearchSpec() {
// Serialization
}
 
public SearchSpec(final String tableId) {
this.tableId = tableId;
}
69,4 → 78,16
public String toJSONString() {
return toJSON().toJSONString();
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.tableId);
out.writeObject(this.content);
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.tableId = in.readUTF();
this.content = (List<SearchContent>) in.readObject();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButtonUnmanaged.java
16,6 → 16,11
import net.minidev.json.JSONObject;
 
public class LightUIButtonUnmanaged extends LightUIElement {
 
public LightUIButtonUnmanaged() {
// Serialization
}
 
public LightUIButtonUnmanaged(final JSONObject json) {
super(json);
}
37,14 → 42,4
super(button);
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIButtonUnmanaged(json);
}
};
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIHTMLStrippedPanel.java
34,16 → 34,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIHTMLStrippedPanel(json);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightUIHTMLStrippedPanel(this);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ActivationOnSelectionControler.java
13,10 → 13,10
package org.openconcerto.ui.light;
 
public class ActivationOnSelectionControler extends LightControler {
public class ActivationOnSelectionControler extends LightController {
 
public ActivationOnSelectionControler(String src, String dest) {
super(LightControler.TYPE_ACTIVATION_ON_SELECTION, src, dest);
super(LightController.TYPE_ACTIVATION_ON_SELECTION, src, dest);
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITab.java
54,17 → 54,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
 
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUITab(json);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightUITab(this);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/InformationLine.java
16,9 → 16,13
import java.awt.Color;
 
public class InformationLine extends LightUILine {
final LightUILabel keyElement;
final LightUILabel valueElement;
private LightUILabel keyElement;
private LightUILabel valueElement;
 
public InformationLine() {
// Serialization
}
 
public InformationLine(final String key, final String value) {
super();
this.setGridAlignment(ALIGN_GRID);
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButton.java
22,8 → 22,12
import net.minidev.json.JSONObject;
 
public class LightUIButton extends LightUIElement {
private transient List<ActionListener> clickListeners = new ArrayList<ActionListener>();
protected transient List<ActionListener> clickListeners = new ArrayList<>();
 
public LightUIButton() {
// Serialization
}
 
public LightUIButton(final JSONObject json) {
super(json);
}
63,11 → 67,13
}
 
public void addClickListener(final ActionListener listener) {
if (!clickListeners.contains(listener)) {
this.clickListeners.add(listener);
}
}
 
public void removeClickListeners() {
this.clickListeners.clear();
public void removeClickListener(final ActionListener listener) {
this.clickListeners.remove(listener);
}
 
public List<ActionListener> getClickListeners() {
75,7 → 81,11
}
 
public void fireClick() {
for (final ActionListener listener : this.clickListeners) {
final List<ActionListener> listeners = new ArrayList<>(this.clickListeners.size());
listeners.addAll(this.clickListeners);
// The list is duplicated to avoid ConcurrentModificationException when a listener destroys
// this button
for (final ActionListener listener : listeners) {
listener.actionPerformed(new ActionEvent(this, 1, "click"));
}
}
83,16 → 93,7
@Override
public void destroy() {
super.destroy();
this.clickListeners.clear();
this.clickListeners = null;
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(JSONObject json) {
return new LightUIButton(json);
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITextArea.java
44,16 → 44,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUITextArea(json);
}
};
}
 
@Override
protected void copy(final LightUIElement element) {
super.copy(element);
if (!(element instanceof LightUITextArea)) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIRawHMTL.java
37,16 → 37,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(JSONObject json) {
return new LightUIRawHMTL(json);
}
};
}
 
@Override
public String getHTML() {
return this.html;
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/SimpleTextLine.java
15,6 → 15,10
 
public class SimpleTextLine extends LightUILine {
 
public SimpleTextLine() {
// Serialization
}
 
public SimpleTextLine(final String text) {
this(text, false, LightUIElement.HALIGN_LEFT);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControl.java
16,6 → 16,11
import net.minidev.json.JSONObject;
 
public abstract class LightUserControl extends LightUIElement {
 
public LightUserControl() {
// Serialization
}
 
public LightUserControl(final JSONObject json) {
super(json);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIContainer.java
15,6 → 15,9
 
import org.openconcerto.utils.io.JSONConverter;
 
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
25,6 → 28,10
public class LightUIContainer extends LightUIElement {
private List<LightUIElement> children = new ArrayList<LightUIElement>(1);
 
public LightUIContainer() {
// Serialization
}
 
public LightUIContainer(final String id) {
super(id);
}
91,6 → 98,11
return this.children.size();
}
 
/**
* Remove all the children from this container
*
* Warning : it doesn't destroy the children
*/
public void clear() {
this.children.clear();
}
273,12 → 285,33
throw new IllegalArgumentException("null element in json parameter");
}
final LightUIElement lightElement = JSONToLightUIConvertorManager.getInstance().createUIElementFromJSON(jsonElement);
this.children.add(lightElement);
this.addChild(lightElement);
}
}
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeInt(this.children.size());
for (LightUIElement e : this.children) {
out.writeObject(e);
}
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
int n = in.readInt();
this.children.clear();
for (int i = 0; i < n; i++) {
LightUIElement e = (LightUIElement) in.readObject();
this.children.add(e);
}
 
}
 
@Override
public void destroy() {
super.destroy();
for (final LightUIElement child : this.children) {
286,13 → 319,4
}
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(JSONObject json) {
return new LightUIContainer(json);
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUISlider.java
15,17 → 15,26
 
import org.openconcerto.utils.io.JSONConverter;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
import java.util.Map;
 
import net.minidev.json.JSONObject;
 
public class LightUISlider extends LightUserControl {
private Integer maxValue = 1;
private Integer minValue = 0;
private Integer increment = 1;
public class LightUISlider extends LightUserControl implements Externalizable {
 
private int minValue = 0;
private int maxValue = 1;
private int increment = 1;
private Map<Integer, String> mapLabels = new HashMap<>();
 
public LightUISlider() {
// Serialization
}
 
public LightUISlider(final String id) {
super(id);
this.setType(TYPE_SLIDER);
149,12 → 158,29
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeInt(this.minValue);
out.writeInt(this.maxValue);
out.writeInt(this.increment);
// map
out.writeInt(this.mapLabels.size());
for (Map.Entry<Integer, String> entry : mapLabels.entrySet()) {
out.writeInt(entry.getKey());
out.writeUTF(entry.getValue());
}
}
 
@Override
public LightUIElement convert(JSONObject json) {
return new LightUISlider(json);
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
this.minValue = in.readInt();
this.maxValue = in.readInt();
this.increment = in.readInt();
this.mapLabels.clear();
int size = in.readInt();
for (int i = 0; i < size; i++) {
this.mapLabels.put(in.readInt(), in.readUTF());
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightController.java
New file
0,0 → 1,103
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
import net.minidev.json.JSONObject;
 
public class LightController implements Externalizable, Transferable {
/**
*
*/
private static final long serialVersionUID = 5894135825924339012L;
private String type, src, dest;
public static final String TYPE_ACTIVATION_ON_SELECTION = "activationOnSelection";
public static final String TYPE_ADD_DEFAULT = "addDefault";
public static final String TYPE_INSERT_DEFAULT = "insertDefault";
public static final String TYPE_COPY = "copy";
public static final String TYPE_REMOVE = "remove";
public static final String TYPE_UP = "up";
public static final String TYPE_DOWN = "down";
public static final String TYPE_CLOSE = "close";
public static final String TYPE_TILT_PREVIOUS = "tilt.previous";
public static final String TYPE_TILT_NEXT = "tilt.next";
 
public LightController() {
// Serialization
}
 
public LightController(final JSONObject json) {
this.fromJSON(json);
}
 
public LightController(final String type, final String src, final String dest) {
this.type = type;
this.src = src;
this.dest = dest;
}
 
public String getType() {
return this.type;
}
 
public String getSrc() {
return this.src;
}
 
public String getDest() {
return this.dest;
}
 
@Override
public String toString() {
return super.getClass().getName() + " : " + this.type + " :" + this.src + "," + this.dest;
}
 
@Override
public JSONObject toJSON() {
final JSONObject result = new JSONObject();
result.put("class", "LightControler");
result.put("type", this.type);
result.put("src", this.src);
result.put("dest", this.dest);
return result;
}
 
@Override
public void fromJSON(final JSONObject json) {
this.type = (String) JSONConverter.getParameterFromJSON(json, "type", String.class);
this.src = (String) JSONConverter.getParameterFromJSON(json, "src", String.class);
this.dest = (String) JSONConverter.getParameterFromJSON(json, "dest", String.class);
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.type);
out.writeUTF(this.src);
out.writeUTF(this.dest);
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.type = in.readUTF();
this.src = in.readUTF();
this.dest = in.readUTF();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUICheckBox.java
17,6 → 17,10
 
public class LightUICheckBox extends LightUserControl {
 
public LightUICheckBox() {
// for serialization
}
 
public LightUICheckBox(final JSONObject json) {
super(json);
}
49,16 → 53,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUICheckBox(json);
}
};
}
 
@Override
public Object getValueForContext() {
return this.isChecked();
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFileUploadWithSelection.java
22,8 → 22,7
private static final String TABLE_ID_JSON_KEY = "table-id";
private String tableId;
 
public LightUIFileUploadWithSelection(final JSONObject json) {
super(json);
public LightUIFileUploadWithSelection() {
}
 
public LightUIFileUploadWithSelection(final LightUIFileUploadWithSelection file) {
39,16 → 38,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIFileUploadWithSelection(json);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightUIFileUploadWithSelection(this);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITextField.java
18,6 → 18,11
import net.minidev.json.JSONObject;
 
public class LightUITextField extends LightUserControl {
 
public LightUITextField() {
// Serialization
}
 
public LightUITextField(final String id) {
super(id);
this.setType(TYPE_TEXT_FIELD);
33,16 → 38,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUITextField(json);
}
};
}
 
@Override
public Object getValueForContext() {
if (this.getValue() == null || this.getValue().trim().isEmpty()) {
return null;
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIDropDownButton.java
32,13 → 32,4
super(dropDownElement);
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIDropDownButton(json);
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIElement.java
18,11 → 18,15
import org.openconcerto.utils.io.Transferable;
 
import java.awt.Color;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintStream;
 
import net.minidev.json.JSONObject;
 
public abstract class LightUIElement implements Transferable {
public abstract class LightUIElement implements Transferable, Externalizable {
 
private static final String HORIZONTALLY_RESIZABLE = "horizontally-resizable";
private static final String VERTICALLY_SCROLLABLE = "vertically-scrollable";
84,6 → 88,8
public static final int TYPE_COLOR_PICKER = 32;
public static final int TYPE_HOUR_EDITOR = 33;
public static final int TYPE_RADIO_BUTTONS = 34;
public static final int TYPE_PASSWORD_FIELD = 35;
 
// valueType
public static final int VALUE_TYPE_STRING = 0;
public static final int VALUE_TYPE_INTEGER = 1;
182,13 → 188,28
private Color foreColor;
 
private LightUIElement parent;
private boolean destroyed;
 
public LightUIElement() {
// Serialization
}
 
public LightUIElement(final String id) {
this.id = id;
this.UUID = java.util.UUID.randomUUID().toString();
this.UUID = getNextUUID();// java.util.UUID.randomUUID().toString();
}
 
JSONToLightUIConvertorManager.getInstance().put(this.getClassName(), this.getConvertor());
private static long count = 0;
private static Integer counterLock = new Integer(123456);
 
private String getNextUUID() {
String result;
synchronized (counterLock) {
count++;
result = "UUID" + count;
}
return result;
}
 
// Init from json constructor
public LightUIElement(final JSONObject json) {
522,10 → 543,6
return this.UUID;
}
 
public void changeUUID() {
this.UUID = java.util.UUID.randomUUID().toString();
}
 
public String getLabel() {
return this.label;
}
727,54 → 744,54
}
 
public final String getTypeAsString() {
String type = "?";
String result = "?";
if (this.type == TYPE_CHECKBOX) {
type = "checkbox";
result = "checkbox";
} else if (this.type == TYPE_COMBOBOX) {
type = "combobox";
result = "combobox";
} else if (this.type == TYPE_LABEL) {
type = "label";
result = "label";
} else if (this.type == TYPE_TEXT_AREA) {
type = "textarea";
result = "textarea";
} else if (this.type == TYPE_TEXT_FIELD) {
type = "textfield";
result = "textfield";
} else if (this.type == TYPE_TABLE) {
type = "table";
result = "table";
} else if (this.type == TYPE_TABBED_UI) {
type = "tabs";
result = "tabs";
} else if (this.type == TYPE_TREE) {
type = "tree";
result = "tree";
} else if (this.type == TYPE_BUTTON) {
type = "button";
result = "button";
} else if (this.type == TYPE_BUTTON_WITH_CONTEXT) {
type = "button with context";
result = "button with context";
} else if (this.type == TYPE_BUTTON_CANCEL) {
type = "cancel button";
result = "cancel button";
} else if (this.type == TYPE_COMBOBOX_ELEMENT) {
type = "combo element";
result = "combo element";
} else if (this.type == TYPE_BUTTON_WITH_SELECTION_CONTEXT) {
type = "button with selection context";
result = "button with selection context";
} else if (this.type == TYPE_FILE_UPLOAD_WITH_SELECTION) {
type = "file upload with selection";
result = "file upload with selection";
} else if (this.type == TYPE_FRAME) {
type = "frame";
result = "frame";
} else if (this.type == TYPE_DROPDOWN_BUTTON) {
type = "drop down button";
result = "drop down button";
} else if (this.type == TYPE_IMAGE) {
type = "image";
result = "image";
} else if (this.type == TYPE_LIST) {
type = "list";
result = "list";
} else if (this.type == TYPE_RAW_HTML) {
type = "raw html";
result = "raw html";
} else if (this.type == TYPE_SLIDER) {
type = "slider";
result = "slider";
} else if (this.type == TYPE_PICTURE_UPLOAD) {
type = "picture upload";
result = "picture upload";
} else if (this.type == TYPE_FILE_UPLOAD) {
type = "file upload";
result = "file upload";
}
 
return type;
return result;
}
 
public String getClassName() {
781,8 → 798,6
return this.getClass().getName();
}
 
public abstract JSONToLightUIConvertor getConvertor();
 
/**
* { return new JSONToLightUIConvertor() {
*
851,8 → 866,12
 
@Override
public String toString() {
if (!destroyed) {
return super.toString() + " " + this.id;
} else {
return super.toString() + " " + this.id + " DESTROYED";
}
}
 
@Override
public JSONObject toJSON() {
878,10 → 897,15
if (this.weightX != DEFAULT_WEIGHT_X) {
result.put("weight-x", this.weightX);
}
 
if (this.weightY != DEFAULT_WEIGHT_Y) {
result.put("weight-y", this.weightY);
}
if (this.commitMode != null) {
result.put("commit-mode", this.commitMode);
}
if (this.width != null) {
result.put("width", this.width);
}
if (this.height != null) {
result.put("height", this.height);
}
928,9 → 952,6
if (this.valueType != null) {
result.put("value-type", this.valueType);
}
if (this.width != null) {
result.put("width", this.width);
}
 
if (!this.enabled) {
result.put("enabled", false);
1040,10 → 1061,12
this.horizontalAlignment = JSONConverter.getParameterFromJSON(json, "horizontal-alignment", Integer.class, HALIGN_LEFT);
this.verticalAlignment = JSONConverter.getParameterFromJSON(json, "vertical-alignment", Integer.class, VALIGN_TOP);
this.weightX = JSONConverter.getParameterFromJSON(json, "weight-x", Integer.class, DEFAULT_WEIGHT_X);
this.weightY = JSONConverter.getParameterFromJSON(json, "weight-y", Integer.class, DEFAULT_WEIGHT_Y);
this.minInputSize = JSONConverter.getParameterFromJSON(json, "min-input-size", Integer.class);
this.type = JSONConverter.getParameterFromJSON(json, "type", Integer.class);
this.valueType = JSONConverter.getParameterFromJSON(json, "value-type", Integer.class);
this.width = JSONConverter.getParameterFromJSON(json, "width", Integer.class);
this.height = JSONConverter.getParameterFromJSON(json, "height", Integer.class);
this.marginTop = JSONConverter.getParameterFromJSON(json, "m-top", Integer.class);
this.marginBottom = JSONConverter.getParameterFromJSON(json, "m-bottom", Integer.class);
this.marginLeft = JSONConverter.getParameterFromJSON(json, "m-left", Integer.class);
1050,7 → 1073,7
this.marginRight = JSONConverter.getParameterFromJSON(json, "m-right", Integer.class);
this.maxWidth = JSONConverter.getParameterFromJSON(json, "max-width", Integer.class);
this.minWidth = JSONConverter.getParameterFromJSON(json, "min-width", Integer.class);
this.height = JSONConverter.getParameterFromJSON(json, "height", Integer.class);
 
this.maxHeight = JSONConverter.getParameterFromJSON(json, "max-height", Integer.class);
this.minHeight = JSONConverter.getParameterFromJSON(json, "min-height", Integer.class);
this.paddingTop = JSONConverter.getParameterFromJSON(json, "p-top", Integer.class);
1081,5 → 1104,241
 
public void destroy() {
this.setValue(null);
this.parent = null;
this.destroyed = true;
}
 
public boolean isDestroyed() {
return destroyed;
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(this.type);
if (this.id == null) {
throw new IllegalStateException("null id");
}
out.writeUTF(this.id);
if (this.UUID == null) {
throw new IllegalStateException("UUID");
}
out.writeUTF(this.UUID);
 
writeIfNotNull(out, this.label);
writeIfNotNull(out, this.value);
 
out.writeInt(this.fontSize);
out.writeInt(this.gridWidth);
out.writeInt(this.gridHeight);
 
out.writeByte(this.horizontalAlignment);
out.writeByte(this.verticalAlignment);
out.writeByte(this.weightX);
out.writeByte(this.weightY);
 
writeIfNotNull(out, this.commitMode);
writeIfNotNull(out, this.width);
writeIfNotNull(out, this.height);
 
// Margins
writeIfNotNull(out, this.marginBottom);
writeIfNotNull(out, this.marginLeft);
writeIfNotNull(out, this.marginRight);
writeIfNotNull(out, this.marginTop);
//
writeIfNotNull(out, this.maxHeight);
writeIfNotNull(out, this.maxWidth);
 
writeIfNotNull(out, this.minInputSize);
writeIfNotNull(out, this.minHeight);
writeIfNotNull(out, this.minWidth);
 
// Paddings
writeIfNotNull(out, this.paddingBottom);
writeIfNotNull(out, this.paddingLeft);
writeIfNotNull(out, this.paddingRight);
writeIfNotNull(out, this.paddingTop);
 
writeIfNotNull(out, this.valueType);
 
out.writeBoolean(this.enabled);
out.writeBoolean(this.fillWidth);
out.writeBoolean(this.fillHeight);
out.writeBoolean(this.foldable);
out.writeBoolean(this.folded);
out.writeBoolean(this.fontBold);
out.writeBoolean(this.fontItalic);
out.writeBoolean(this.horizontallyResizable);
out.writeBoolean(this.horizontallyScrollable);
out.writeBoolean(this.required);
out.writeBoolean(this.readOnly);
out.writeBoolean(this.verticallyResizable);
out.writeBoolean(this.verticallyScrollable);
 
out.writeBoolean(this.visible);
out.writeBoolean(this.notSaved);
 
writeIfNotNull(out, this.displayPrecision);
writeIfNotNull(out, this.icon);
writeIfNotNull(out, this.toolTip);
writeIfNotNull(out, this.valuePrecision);
writeIfNotNull(out, this.valueRange);
 
// Colors
writeIfNotNull(out, this.backgroundColor);
writeIfNotNull(out, this.borderColor);
writeIfNotNull(out, this.cellBackgroundColor);
writeIfNotNull(out, this.foreColor);
}
 
void writeIfNotNull(ObjectOutput out, String s) throws IOException {
if (s != null) {
out.writeBoolean(true);
out.writeUTF(s);
} else {
out.writeBoolean(false);
}
}
 
void writeIfNotNull(ObjectOutput out, Integer s) throws IOException {
if (s != null) {
out.writeBoolean(true);
out.writeInt(s);
} else {
out.writeBoolean(false);
}
}
 
void writeIfNotNull(ObjectOutput out, Color s) throws IOException {
if (s != null) {
out.writeBoolean(true);
out.writeInt(s.getRGB());
} else {
out.writeBoolean(false);
}
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.type = in.readInt();
this.id = in.readUTF();
this.UUID = in.readUTF();
if (in.readBoolean()) {
this.label = in.readUTF();
}
if (in.readBoolean()) {
this.value = in.readUTF();
}
this.fontSize = in.readInt();
this.gridWidth = in.readInt();
this.gridHeight = in.readInt();
this.horizontalAlignment = in.readByte();
this.verticalAlignment = in.readByte();
this.weightX = in.readByte();
this.weightY = in.readByte();
 
if (in.readBoolean()) {
this.commitMode = in.readInt();
}
if (in.readBoolean()) {
this.width = in.readInt();
}
if (in.readBoolean()) {
this.height = in.readInt();
}
// Margins
if (in.readBoolean()) {
this.marginBottom = in.readInt();
}
if (in.readBoolean()) {
this.marginLeft = in.readInt();
}
if (in.readBoolean()) {
this.marginRight = in.readInt();
}
if (in.readBoolean()) {
this.marginTop = in.readInt();
}
 
//
if (in.readBoolean()) {
this.maxHeight = in.readInt();
}
if (in.readBoolean()) {
this.maxWidth = in.readInt();
}
 
if (in.readBoolean()) {
this.minInputSize = in.readInt();
}
if (in.readBoolean()) {
this.minHeight = in.readInt();
}
if (in.readBoolean()) {
this.minWidth = in.readInt();
}
 
// Paddings
if (in.readBoolean()) {
this.paddingBottom = in.readInt();
}
if (in.readBoolean()) {
this.paddingLeft = in.readInt();
}
if (in.readBoolean()) {
this.paddingRight = in.readInt();
}
if (in.readBoolean()) {
this.paddingTop = in.readInt();
}
if (in.readBoolean()) {
this.valueType = in.readInt();
}
 
this.enabled = in.readBoolean();
this.fillWidth = in.readBoolean();
this.fillHeight = in.readBoolean();
this.foldable = in.readBoolean();
this.folded = in.readBoolean();
this.fontBold = in.readBoolean();
this.fontItalic = in.readBoolean();
this.horizontallyResizable = in.readBoolean();
this.horizontallyScrollable = in.readBoolean();
this.required = in.readBoolean();
this.readOnly = in.readBoolean();
this.verticallyResizable = in.readBoolean();
this.verticallyScrollable = in.readBoolean();
 
//
this.visible = in.readBoolean();
this.notSaved = in.readBoolean();
if (in.readBoolean()) {
this.displayPrecision = in.readUTF();
}
if (in.readBoolean()) {
this.icon = in.readUTF();
}
if (in.readBoolean()) {
this.toolTip = in.readUTF();
}
if (in.readBoolean()) {
this.valuePrecision = in.readUTF();
}
if (in.readBoolean()) {
this.valueRange = in.readUTF();
}
// Colors
if (in.readBoolean()) {
this.backgroundColor = new Color(in.readInt());
}
if (in.readBoolean()) {
this.borderColor = new Color(in.readInt());
}
if (in.readBoolean()) {
this.cellBackgroundColor = new Color(in.readInt());
}
if (in.readBoolean()) {
this.foreColor = new Color(in.readInt());
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIAutoCompleteComboBox.java
New file
0,0 → 1,94
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import java.util.List;
import java.util.Optional;
 
import net.minidev.json.JSONObject;
 
public class LightUIAutoCompleteComboBox extends LightUIComboBox {
private static final String FILTER = "filter";
 
private String filter;
 
private LightUIComboRequest request;
 
public LightUIAutoCompleteComboBox() {
}
 
public LightUIAutoCompleteComboBox(final String id) {
super(id);
this.setType(TYPE_AUTOCOMPLETE_COMBOBOX);
}
 
public void setComboRequest(final LightUIComboRequest request) {
this.request = request;
this.setFilter("");
this.setAlreadyFilled(true);
}
 
public LightUIComboRequest getComboRequest() {
return this.request;
}
 
public String getFilter() {
return this.filter;
}
 
public void setFilter(final String filter) {
this.filter = filter;
this.applyFilter();
}
 
private void applyFilter() {
if (this.request != null) {
Integer selectedId = null;
if (this.hasSelectedValue()) {
selectedId = this.getSelectedValue().getId();
}
 
this.clearValues();
if (this.hasNotSpecifedLine()) {
this.addValue(LightUIComboBox.getDefaultValue());
}
 
final Optional<LightUIComboBoxElement> sel = Optional.ofNullable(this.hasSelectedValue() ? this.getSelectedValue() : null);
final List<LightUIComboBoxElement> items = this.request.getItems(this.filter, sel);
 
for (final LightUIComboBoxElement item : items) {
this.addValue(item);
}
 
this.setSelectedId(selectedId);
}
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put(FILTER, this.filter);
return json;
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
this.filter = JSONConverter.getParameterFromJSON(json, FILTER, String.class);
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIComboBox.java
16,6 → 16,9
import org.openconcerto.utils.i18n.TranslationManager;
import org.openconcerto.utils.io.JSONConverter;
 
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
 
33,8 → 36,12
 
private LightUIComboBoxElement selectedValue = null;
 
private List<LightUIComboBoxElement> values = new ArrayList<LightUIComboBoxElement>();
private List<LightUIComboBoxElement> values = new ArrayList<>();
 
public LightUIComboBox() {
// Serialization
}
 
// Init from json constructor
public LightUIComboBox(final JSONObject json) {
super(json);
138,16 → 145,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIComboBox(json);
}
};
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
 
215,6 → 212,45
@Override
public void destroy() {
super.destroy();
this.clearValues();
this.selectedValue = null;
this.values = null;
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeBoolean(this.alreadyFilled);
out.writeBoolean(this.hasNotSpecifedLine);
 
if (this.selectedValue != null) {
out.writeBoolean(true);
this.selectedValue.writeExternal(out);
} else {
out.writeBoolean(false);
}
out.writeInt(this.values.size());
for (LightUIComboBoxElement e : this.values) {
e.writeExternal(out);
}
 
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
this.alreadyFilled = in.readBoolean();
this.hasNotSpecifedLine = in.readBoolean();
if (in.readBoolean()) {
this.selectedValue = new LightUIComboBoxElement();
this.selectedValue.readExternal(in);
}
int size = in.readInt();
this.values.clear();
for (int i = 0; i < size; i++) {
LightUIComboBoxElement e = new LightUIComboBoxElement();
e.readExternal(in);
this.values.add(e);
}
 
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIComboRequest.java
New file
0,0 → 1,21
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import java.util.List;
import java.util.Optional;
 
public interface LightUIComboRequest {
public List<LightUIComboBoxElement> getItems(final String filter, final Optional<LightUIComboBoxElement> selection);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIColorPicker.java
55,13 → 55,4
return this.getValue();
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(JSONObject json) {
return new LightUIColorPicker(json);
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIImage.java
31,14 → 31,4
super(image);
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIImage(json);
}
};
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ColumnSpec.java
45,7 → 45,7
// Default value (to add a new line)
private Object defaultValue;
 
private Integer horizontalAlignment = null;
private Integer horizontalAlignment = LightUIElement.HALIGN_LEFT;
 
private double width;
private double maxWidth;
119,6 → 119,11
return this.horizontalAlignment;
}
 
/**
*
* @param horizontalAlignment LightUIElement.HALIGN_LEFT, LightUIElement.HALIGN_RIGHT or
* LightUIElement.HALIGN_CENTER
*/
public void setHorizontalAlignment(final int horizontalAlignment) {
this.horizontalAlignment = horizontalAlignment;
}
184,6 → 189,7
this.width = 200;
this.maxWidth = 500;
this.minWidth = 50;
this.horizontalAlignment = LightUIElement.HALIGN_LEFT;
}
 
public Element createXmlColumnPref() {
243,7 → 249,7
}
result.put(VALUE_CLASS, JSONConverter.getJSON(this.valueClass));
 
if (this.horizontalAlignment != null) {
if (this.horizontalAlignment != LightUIElement.HALIGN_LEFT) {
result.put(HORIZONTAL_ALIGNMENT, this.horizontalAlignment);
}
return result;
271,7 → 277,9
 
this.editable = JSONConverter.getParameterFromJSON(json, EDITABLE, Boolean.class, Boolean.FALSE);
this.horizontalAlignment = JSONConverter.getParameterFromJSON(json, HORIZONTAL_ALIGNMENT, Integer.class);
 
if (this.horizontalAlignment == null) {
this.horizontalAlignment = LightUIElement.HALIGN_LEFT;
}
final JSONObject jsonDefaultValue = JSONConverter.getParameterFromJSON(json, DEFAULT_VALUE, JSONObject.class);
// TODO: implement default value
 
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIComboBoxElement.java
16,9 → 16,14
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
import net.minidev.json.JSONObject;
 
public class LightUIComboBoxElement implements Transferable {
public class LightUIComboBoxElement implements Transferable, Externalizable {
/**
* ID of row, must be unique for a LightUIComboBox. It's represent the SQLRow ID when the
* LightUICombo is attach to a field in database
29,6 → 34,10
private String value2 = null;
private String icon = null;
 
public LightUIComboBoxElement() {
// Serialization
}
 
public LightUIComboBoxElement(final JSONObject json) {
this.fromJSON(json);
}
108,9 → 117,38
 
@Override
public void fromJSON(final JSONObject json) {
this.id = (Integer) JSONConverter.getParameterFromJSON(json, "id", Integer.class);
this.value1 = (String) JSONConverter.getParameterFromJSON(json, "value1", String.class);
this.value2 = (String) JSONConverter.getParameterFromJSON(json, "value2", String.class);
this.icon = (String) JSONConverter.getParameterFromJSON(json, "icon", String.class);
this.id = JSONConverter.getParameterFromJSON(json, "id", Integer.class);
this.value1 = JSONConverter.getParameterFromJSON(json, "value1", String.class);
this.value2 = JSONConverter.getParameterFromJSON(json, "value2", String.class);
this.icon = JSONConverter.getParameterFromJSON(json, "icon", String.class);
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// TODO : value1,value2 et icon -> "" par défaut
if (value1 == null)
value1 = "";
if (value2 == null)
value2 = "";
if (icon == null)
icon = "";
out.writeInt(id);
out.writeUTF(value1);
out.writeUTF(value2);
out.writeUTF(icon);
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.value1 = in.readUTF();
this.value2 = in.readUTF();
this.icon = in.readUTF();
 
}
 
@Override
public String toString() {
return id + ":" + value1 + ":" + value2;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButtonWithSelectionContext.java
19,7 → 19,7
 
public class LightUIButtonWithSelectionContext extends LightUIButton {
 
RowSelectionSpec tableSelection;
private RowSelectionSpec tableSelection;
 
public LightUIButtonWithSelectionContext(final JSONObject json) {
super(json);
34,6 → 34,12
super(button);
}
 
@Override
public void destroy() {
super.destroy();
this.tableSelection = null;
}
 
public RowSelectionSpec getTableSelection() {
return this.tableSelection;
}
52,8 → 58,9
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
this.tableSelection = (RowSelectionSpec) JSONConverter.getParameterFromJSON(json, "table-selection", RowSelectionSpec.class);
this.tableSelection = new RowSelectionSpec();
final JSONObject o = (JSONObject) json.get("table-selection");
this.tableSelection.fromJSON(o);
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITable.java
18,6 → 18,10
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
 
29,7 → 33,7
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class LightUITable extends LightUserControlContainer {
public class LightUITable extends LightUserControlContainer implements Externalizable {
 
public static final int DEFAULT_LINE_HEIGHT = 40;
 
52,6 → 56,10
 
private int lineHeight = DEFAULT_LINE_HEIGHT;
 
public LightUITable() {
// Serialization
}
 
// Init from json constructor
public LightUITable(final JSONObject json) {
super(json);
186,6 → 194,10
return this.getTableSpec().getContent().setRow(index, row);
}
 
public final void insertRow(final int index, final Row row) {
this.getTableSpec().getContent().insertRow(index, row);
}
 
public final boolean addRow(final Row row) {
return this.getTableSpec().getContent().addRow(row);
}
224,7 → 236,7
}
 
public final void clearSelection(final boolean fire) {
this.getTableSpec().getSelection().getIds().clear();
this.getTableSpec().getSelection().clear();
if (fire) {
this.fireSelectionChange();
}
491,23 → 503,13
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUITable(json);
}
};
}
 
@Override
public void _setValueFromContext(final Object value) {
if (value != null) {
final JSONArray jsonContext = (JSONArray) JSONConverter.getObjectFromJSON(value, JSONArray.class);
final JSONArray jsonContext = JSONConverter.getObjectFromJSON(value, JSONArray.class);
final ColumnsSpec columnsSpec = this.getTableSpec().getColumns();
final int columnsCount = columnsSpec.getColumnCount();
 
final List<Integer> editorsIndex = new ArrayList<Integer>();
final List<Integer> editorsIndex = new ArrayList<>();
 
for (int i = 0; i < columnsCount; i++) {
final ColumnSpec columnSpec = columnsSpec.getColumn(i);
609,6 → 611,29
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
 
out.writeBoolean(dynamicLoad);
out.writeBoolean(allowSelection);
out.writeBoolean(allowMultiSelection);
out.writeBoolean(autoSelectFirstLine);
tableSpec.writeExternal(out);
 
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
this.dynamicLoad = in.readBoolean();
this.allowSelection = in.readBoolean();
this.allowMultiSelection = in.readBoolean();
this.autoSelectFirstLine = in.readBoolean();
tableSpec = new TableSpec();
tableSpec.readExternal(in);
}
 
@Override
public void destroy() {
super.destroy();
this.selectionListeners.clear();
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TreeItem.java
18,6 → 18,8
import java.util.ArrayList;
import java.util.List;
 
import net.minidev.json.JSONObject;
 
public class TreeItem implements Serializable {
/**
*
34,6 → 36,10
public TreeItem() {
}
 
public TreeItem(JSONObject o) {
// TODO json..
}
 
public TreeItem(String id, String label) {
this.id = id;
this.label = label;
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TableContent.java
13,15 → 13,20
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class TableContent implements Transferable {
public class TableContent implements Transferable, Externalizable {
private static final long serialVersionUID = 3648381615123520834L;
private String tableId;
private List<Row> rows;
71,6 → 76,10
return this.rows.set(index, row);
}
 
public final synchronized void insertRow(final int index, final Row row) {
this.rows.add(index, row);
}
 
public final synchronized Row removeRow(final int index) {
return this.rows.remove(index);
}
120,4 → 129,28
this.setRows(listRows);
}
}
 
@Override
public synchronized void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(tableId);
out.writeInt(rows.size());
for (Row row : this.rows) {
row.writeExternal(out);
}
 
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
tableId = in.readUTF();
int size = in.readInt();
this.rows = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
final Row row = new Row();
row.readExternal(in);
this.rows.add(row);
}
 
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/TextAreaTableCellEditor.java
14,8 → 14,8
package org.openconcerto.ui;
 
import org.openconcerto.utils.text.DocumentFilterList;
import org.openconcerto.utils.text.DocumentFilterList.FilterType;
import org.openconcerto.utils.text.LimitedSizeDocumentFilter;
import org.openconcerto.utils.text.DocumentFilterList.FilterType;
 
import java.awt.Color;
import java.awt.Component;
177,7 → 177,9
Rectangle r = null;
try {
r = textArea.modelToView(s.length());
} catch (BadLocationException e) {
} catch (BadLocationException | IllegalArgumentException e2) {
// java.lang.IllegalArgumentException: TextHitInfo is out of range
// from java.awt.font.TextLayout.checkTextHit(Unknown Source)
return -1;
}
int h = 0;
186,7 → 188,6
final int newHeight = r.y + r.height + 2;
h = Math.max(textArea.getMinimumSize().height, newHeight);
}
// System.out.println("TextAreaTableCellEditor.getHeightFor() h:" + h);
return h;
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/group/Group.java
41,6 → 41,8
private int order;
private final List<Tuple2<Item, Integer>> list;
 
private String tabId;
 
public Group(final String id) {
this(id, LayoutHints.DEFAULT_GROUP_HINTS);
}
296,4 → 298,12
}
return true;
}
 
public void setTabId(String tabId) {
this.tabId = tabId;
}
 
public String getTabId() {
return this.tabId;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/list/selection/ListSelectionState.java
204,6 → 204,7
final int[] interval = intervals.get(0);
this.getSelModel().setSelectionInterval(interval[0], interval[1]);
} else {
// ATTN minimize calls to the enclosing method as the code below is slow
this.getSelModel().setValueIsAdjusting(true);
this.getSelModel().clearSelection();
for (final int[] interval : intervals) {
/trunk/OpenConcerto/src/org/openconcerto/ui/state/JTableStateManager.java
18,6 → 18,7
import org.openconcerto.ui.table.XTableColumnModel;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.TableSorter;
import org.openconcerto.utils.TableSorter.Directive;
 
import java.io.File;
import java.io.IOException;
24,6 → 25,7
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
 
import javax.swing.JTable;
import javax.swing.event.AncestorEvent;
31,9 → 33,11
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
54,6 → 58,9
public class JTableStateManager extends ListenerXMLStateManager<JTable, AncestorListener> {
 
private static final String VERSION = "20100810";
private static final String IDENTIFIER_ATTR = "identifier";
private static final String MODEL_INDEX_ATTR = "modelIndex";
private static final String SORT_ATTR = "sort";
 
public JTableStateManager(JTable table) {
this(table, null);
72,9 → 79,11
return new AncestorListener() {
 
public void ancestorAdded(AncestorEvent event) {
// nothing
}
 
public void ancestorMoved(AncestorEvent event) {
// nothing
}
 
public void ancestorRemoved(AncestorEvent event) {
113,9 → 122,9
doc.appendChild(elem);
 
final TableColumnModel model = this.getSrc().getColumnModel();
final XTableColumnModel visibilityModel = model instanceof XTableColumnModel ? (XTableColumnModel) model : null;
final TableModel tModel = this.getSrc().getModel();
if (model instanceof XTableColumnModel) {
final XTableColumnModel visibilityModel = (XTableColumnModel) model;
if (visibilityModel != null) {
for (final TableColumn col : visibilityModel.getColumns(false)) {
writeCol(elem, col, tModel).setAttribute("visible", String.valueOf(visibilityModel.isColumnVisible(col)));
}
127,10 → 136,31
}
}
 
if (tModel instanceof TableSorter) {
TableSorter sorter = (TableSorter) tModel;
final Element sortingColsElem = doc.createElement("sortingColumns");
elem.appendChild(sortingColsElem);
for (final Directive d : sorter.getSortingColumns()) {
final Element colElem = doc.createElement("sortCol");
sortingColsElem.appendChild(colElem);
final TableColumn col;
if (visibilityModel != null) {
col = visibilityModel.getColumnByModelIndex(d.getColumn());
} else {
col = model.getColumn(getSrc().convertColumnIndexToView(d.getColumn()));
}
colElem.setAttribute(IDENTIFIER_ATTR, String.valueOf(col.getIdentifier()));
final int status = d.getDirection();
setSortAttribute(colElem, status);
}
}
 
// Use a Transformer for output
final TransformerFactory tFactory = TransformerFactory.newInstance();
try {
tFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
final Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(elem), new StreamResult(out));
} catch (TransformerException e) {
throw new IOException("Couldn't output " + doc, e);
146,19 → 176,33
res.setAttribute("min", String.valueOf(min));
res.setAttribute("max", String.valueOf(max));
res.setAttribute("width", String.valueOf(width));
res.setAttribute("identifier", String.valueOf(col.getIdentifier()));
res.setAttribute("modelIndex", String.valueOf(col.getModelIndex()));
if (tModel instanceof TableSorter) {
TableSorter sorter = (TableSorter) tModel;
int status = sorter.getSortingStatus(col.getModelIndex());
res.setAttribute(IDENTIFIER_ATTR, String.valueOf(col.getIdentifier()));
res.setAttribute(MODEL_INDEX_ATTR, String.valueOf(col.getModelIndex()));
return res;
}
 
protected final void setSortAttribute(final Element res, final int status) {
if (status == TableSorter.ASCENDING) {
res.setAttribute("sort", "ascending");
res.setAttribute(SORT_ATTR, "ascending");
} else if (status == TableSorter.DESCENDING) {
res.setAttribute("sort", "descending");
res.setAttribute(SORT_ATTR, "descending");
}
}
return res;
 
protected final int getSortAttribute(final NamedNodeMap attrs) {
final Node sortNode = attrs.getNamedItem(SORT_ATTR);
if (sortNode != null) {
final String sort = sortNode.getNodeValue();
if (sort.equals("ascending")) {
return TableSorter.ASCENDING;
} else if (sort.equals("descending")) {
return TableSorter.DESCENDING;
} else {
Log.get().log(Level.INFO, "ignore unknown sort value : {0}", sort);
}
}
return TableSorter.NOT_SORTED;
}
 
/**
* Met les colonnes comme spécifier dans <code>file</code>. Ne fait rien si <code>file</code>
195,11 → 239,11
}
}
final TableModel tModel = this.getSrc().getModel();
final List<TableColumn> invisibleCols = new ArrayList<TableColumn>();
final List<TableColumn> invisibleCols = new ArrayList<>();
for (int i = 0; i < colsCount; i++) {
final NamedNodeMap attrs = listOfCol.item(i).getAttributes();
// index
final int modelIndex = Integer.parseInt(attrs.getNamedItem("modelIndex").getNodeValue());
final int modelIndex = Integer.parseInt(attrs.getNamedItem(MODEL_INDEX_ATTR).getNodeValue());
// move from the current index to the final view index
model.moveColumn(this.getSrc().convertColumnIndexToView(modelIndex), i);
 
234,26 → 278,37
if (visible != null && !Boolean.parseBoolean(visible.getNodeValue()))
invisibleCols.add(modelCol);
 
// Better not to restore sorting at all, than to restore wrong sorting. So remove
// the code that was here since it didn't restore the order of the sorting columns
// (e.g. first sort by country, then by age).
}
 
// Sorting
final NodeList listOfSortCol = doc.getElementsByTagName("sortCol");
final int sortColCount = listOfSortCol.getLength();
if (tModel instanceof TableSorter && sortColCount > 0) {
final List<Directive> sortingCols = new ArrayList<>();
for (int i = 0; i < sortColCount; i++) {
final NamedNodeMap attrs = listOfSortCol.item(i).getAttributes();
 
if (tModel instanceof TableSorter) {
final TableSorter sorter = (TableSorter) tModel;
final Node sortNode = attrs.getNamedItem("sort");
if (sortNode != null) {
String sort = sortNode.getNodeValue();
if (sort != null) {
if (sort.equals("ascending")) {
sorter.setSortingStatus(modelIndex, TableSorter.ASCENDING);
} else if (sort.equals("descending")) {
sorter.setSortingStatus(modelIndex, TableSorter.DESCENDING);
} else {
sorter.setSortingStatus(modelIndex, TableSorter.NOT_SORTED);
final String colID = attrs.getNamedItem(IDENTIFIER_ATTR).getNodeValue();
final int colIndex;
try {
colIndex = model.getColumnIndex(colID);
} catch (Exception e) {
Log.get().log(Level.INFO, "ignore unknown identifier : " + colID, e);
continue;
}
final int modelIndex = model.getColumn(colIndex).getModelIndex();
final int direction = getSortAttribute(attrs);
if (direction == TableSorter.NOT_SORTED) {
Log.get().info("ignore sort value for column " + colID);
continue;
}
sortingCols.add(new Directive(modelIndex, direction));
}
((TableSorter) tModel).setSortingColumns(sortingCols);
}
 
}
if (visibilityModel != null) {
for (final TableColumn toRm : invisibleCols) {
visibilityModel.setColumnVisible(toRm, false);
271,8 → 326,8
final int colsCount = listOfCol.getLength();
for (int i = 0; i < colsCount; i++) {
final NamedNodeMap attrs = listOfCol.item(i).getAttributes();
final int modelIndex = Integer.parseInt(attrs.getNamedItem("modelIndex").getNodeValue());
final String xmlID = attrs.getNamedItem("identifier").getNodeValue();
final int modelIndex = Integer.parseInt(attrs.getNamedItem(MODEL_INDEX_ATTR).getNodeValue());
final String xmlID = attrs.getNamedItem(IDENTIFIER_ATTR).getNodeValue();
final String uiID = String.valueOf(uiCols.get(modelIndex).getIdentifier());
if (!uiID.equals(xmlID))
return false;
/trunk/OpenConcerto/src/org/openconcerto/ui/TimeTextField.java
136,8 → 136,10
final int stop = text.length();
for (int i = 0; i < stop; i++) {
int position = i + offset;
if (position < te.length) {
te[position] = text.charAt(i);
}
}
for (int i = 0; i < stop; i++) {
int position = i + offset;
final char c = text.charAt(i);
156,6 → 158,7
});
// Move cusor to not be on ':'
this.setNavigationFilter(new NavigationFilter() {
 
@Override
public int getNextVisualPositionFrom(JTextComponent text, int pos, Bias bias, int direction, Bias[] biasRet) throws BadLocationException {
if (pos == 1 && direction == SwingConstants.EAST) {
/trunk/OpenConcerto/src/org/openconcerto/ui/table/IconTableCellRenderer.java
21,8 → 21,8
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
 
import javax.swing.AbstractCellEditor;
import javax.swing.JTable;
33,7 → 33,7
public class IconTableCellRenderer extends AbstractCellEditor implements TableCellEditor, TableCellRenderer, ItemListener {
 
private int va;
private final List<JImage> images = new Vector<JImage>();
private final List<JImage> images = new ArrayList<>();
IconCellRenderer renderer;
 
/**
42,20 → 42,17
public IconTableCellRenderer(List<URL> icons) {
for (int i = 0; i < icons.size(); i++) {
URL filenameUrl = icons.get(i);
// System.out.println("Chargement de l'image " + filename);
 
JImage img = new JImage(filenameUrl);
img.setCenterImage(true);
img.check();
img.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
System.out.println(e);
IconTableCellRenderer.this.va++;
if (IconTableCellRenderer.this.va >= IconTableCellRenderer.this.images.size()) {
IconTableCellRenderer.this.va = 0;
}
 
stopCellEditing();
}
});
70,12 → 67,11
value = Integer.valueOf(0);
}
this.va = ((Integer) value).intValue();
final JImage image = this.images.get(this.va);
return image;
return this.images.get(this.va);
}
 
public Object getCellEditorValue() {
return new Integer(this.va);
return Integer.valueOf(this.va);
}
 
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
83,13 → 79,13
}
 
public void itemStateChanged(ItemEvent e) {
System.out.println("IconTableCellRenderer.itemStateChanged()");
// nothing
}
 
}
 
class IconCellRenderer extends DefaultTableCellRenderer {
List<JImage> images;
private List<JImage> images;
 
public IconCellRenderer(List<JImage> images) {
super();
101,6 → 97,10
}
 
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
// Bug remonté sur Linux
value = Integer.valueOf(0);
}
int val = ((Integer) value).intValue();
final JImage image = this.images.get(val);
this.setIcon(image.getImageIcon());
/trunk/OpenConcerto/src/org/openconcerto/ui/EnhancedTableUI.java
100,15 → 100,14
Rectangle clip = g.getClipBounds();
Point upperLeft = clip.getLocation();
Point lowerRight = new Point(clip.x + clip.width - 1, clip.y + clip.height - 1);
int rMin = 0;// table.rowAtPoint(upperLeft);
int rMax = table.getRowCount() - 1;// /table.rowAtPoint(lowerRight);
// This should never happen.
if (rMin == -1) {
int rMin = table.rowAtPoint(upperLeft);
int rMax = table.rowAtPoint(lowerRight);
if (rMin < 0) {
rMin = 0;
}
// If the table does not have enough rows to fill the view we'll get -1.
// Replace this with the index of the last row.
if (rMax == -1) {
if (rMax == -1 || rMax >= table.getRowCount()) {
rMax = table.getRowCount() - 1;
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextCombo.java
55,6 → 55,7
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.DocumentEvent;
137,9 → 138,10
this.cacheLoading = false;
this.modifyingDoc = false;
 
this.setMinimumSize(new Dimension(80, 22));
final int h = new JTextField("12").getPreferredSize().height;
this.setMinimumSize(new Dimension(80, h));
// Test de Preferred Size pour ne pas exploser les GridBagLayouts
this.setPreferredSize(new Dimension(120, 22));
this.setPreferredSize(new Dimension(120, h));
this.setInteractionMode(InteractionMode.READ_WRITE);
 
// ATTN marche car locked est final, sinon il faudrait pouvoir enlever/ajouter les listeners
/trunk/OpenConcerto/src/org/openconcerto/ui/RangedIntegerTableCellEditor.java
21,6 → 21,7
 
public RangedIntegerTableCellEditor(int min, int max) {
super(new JTextField());
((JTextField) getComponent()).setBorder(null);
this.min = min;
this.max = max;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/mapping_fr.xml
1,6 → 1,7
<?xml version="1.0" encoding="UTF-8" ?>
<ROOT>
<TABLE name="USER_COMMON">
<translations>
<element refid="sql.user">
<name base="utilisateur" nounClass="masculine" />
<FIELD name="NOM" label="Nom" titlelabel="Nom" />
<FIELD name="PRENOM" label="Prénom" titlelabel="Prénom" />
<FIELD name="PASSWORD" label="Mot de passe" titlelabel="Mot de passe" />
12,17 → 13,21
<FIELD name="MAIL" label="E-Mail" titlelabel="E-Mail" />
<FIELD name="DISABLED" label="Compte désactivé" />
<FIELD name="TEL" label="Téléphone" titlelabel="Téléphone" />
</TABLE>
<TABLE name="RIGHT">
</element>
<element refid="sql.right">
<name base="droit" nounClass="masculine" />
<FIELD name="CODE" label="Code" titlelabel="Code" />
<FIELD name="NOM" label="Nom" titlelabel="Nom du droit" />
<FIELD name="DESCRIPTION" label="Description" titlelabel="Desc." />
</TABLE>
<TABLE name="USER_RIGHT">
</element>
<element refid="sql.user-right">
<name base="droit utilisateur" nounClass="masculine">
<variant refids="plural" value="droits utilisateurs" />
</name>
<FIELD name="ID_USER_COMMON" label="Utilisateur" titlelabel="Utilis." />
<FIELD name="ID_RIGHT" label="Droit" />
<FIELD name="OBJECT" label="Objet" />
<FIELD name="user.right.parameters.editor" label="Objet" />
<FIELD name="HAVE_RIGHT" label="Droit accordé" titlelabel="Accordé" />
</TABLE>
</ROOT>
</element>
</translations>
/trunk/OpenConcerto/src/org/openconcerto/sql/mapping_es.xml
New file
0,0 → 1,14
<?xml version="1.0" encoding="UTF-8" ?>
<translations>
<element refid="sql.user">
<name base="usuario" nounClass="masculine" />
</element>
<element refid="sql.right">
<name base="derecho" nounClass="masculine" />
</element>
<element refid="sql.user-right">
<name base="derecho de usuario" nounClass="masculine">
<variant refids="plural" value="derechos de usuarios" />
</name>
</element>
</translations>
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ITextArticleWithCompletionCellEditor.java
13,6 → 13,12
package org.openconcerto.sql.sqlobject;
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.cc.ITransformer;
 
import java.awt.Component;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
23,12 → 29,6
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellEditor;
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.cc.ITransformer;
 
public class ITextArticleWithCompletionCellEditor extends AbstractCellEditor implements TableCellEditor {
 
private final ITextArticleWithCompletion text;
37,6 → 37,7
public ITextArticleWithCompletionCellEditor(SQLTable tableArticle, SQLTable tableARticleFournisseur) {
this.text = new ITextArticleWithCompletion(tableArticle, tableARticleFournisseur);
this.text.setBorder(BorderFactory.createEmptyBorder());
this.text.getTextComp().setBorder(BorderFactory.createEmptyBorder());
}
 
private void initListener(final JTable t) {
/trunk/OpenConcerto/src/org/openconcerto/sql/Configuration.java
126,7 → 126,9
 
public abstract SQLFilter getFilter();
 
public abstract SQLFieldTranslator getTranslator();
public final SQLFieldTranslator getTranslator() {
return this.getDirectory().getTranslator();
}
 
public abstract SQLElementDirectory getDirectory();
 
207,15 → 209,13
}
 
/**
* Add the translator and directory of <code>o</code> to this.
* Add the directory of <code>o</code> to this.
*
* @param o the configuration to add.
* @return this.
* @see SQLFieldTranslator#putAll(SQLFieldTranslator)
* @see SQLElementDirectory#putAll(SQLElementDirectory)
*/
public Configuration add(Configuration o) {
this.getTranslator().putAll(o.getTranslator());
this.getDirectory().putAll(o.getDirectory());
return this;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLFieldTranslator.java
17,6 → 17,7
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.element.SQLElementDirectory.DirectoryListener;
import org.openconcerto.sql.element.SQLElementNamesFromXML;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
34,10 → 35,10
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.i18n.Phrase;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
49,6 → 50,7
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.prefs.Preferences;
 
139,6 → 141,9
// { SQLTable -> { compCode, variant, item -> RowItemDesc }}
@GuardedBy("this")
private final Map<SQLTable, Map<List<String>, RowItemDesc>> translation;
// { element code -> { variant -> name }}
@GuardedBy("this")
private final Map<String, Map<String, Phrase>> elementNames;
private final SQLTable table;
private final SQLElementDirectory dir;
@GuardedBy("this")
146,6 → 151,7
 
{
this.translation = new HashMap<SQLTable, Map<List<String>, RowItemDesc>>();
this.elementNames = new HashMap<>();
this.unknownCodes = new HashSet<String>();
}
 
153,10 → 159,9
* Create a new instance.
*
* @param root the default root for tables.
* @param inputStream the XML, can be <code>null</code>.
* @param dir the directory where to look for tables not in <code>root</code>.
*/
public SQLFieldTranslator(DBRoot root, InputStream inputStream, SQLElementDirectory dir) {
public SQLFieldTranslator(DBRoot root, SQLElementDirectory dir) {
try {
this.table = getMetaTable(root);
} catch (SQLException e) {
180,8 → 185,6
}
}
});
if (inputStream != null)
this.load(root, inputStream);
fetchAndPut(this.table, null);
}
 
217,10 → 220,10
}
}
 
public void load(DBRoot b, File file) {
try {
load(b, new FileInputStream(file));
} catch (FileNotFoundException e) {
public void load(DBRoot b, File file, final SQLElementNamesFromXML elemNames) {
try (final InputStream ins = new FileInputStream(file)) {
this.load(b, ins, elemNames);
} catch (IOException e) {
e.printStackTrace();
}
}
229,14 → 232,8
return elem.getChildren();
}
 
/**
* Load more translations.
*
* @param b the default root for tables.
* @param inputStream the XML.
*/
public void load(DBRoot b, InputStream inputStream) {
this.load(b, CORE_VARIANT, inputStream);
public Tuple2<Set<SQLTable>, Set<String>> load(DBRoot b, InputStream inputStream, final SQLElementNamesFromXML elemNames) {
return this.load(b, CORE_VARIANT, inputStream, elemNames);
}
 
/**
245,9 → 242,10
* @param b the default root for tables.
* @param variant the variant to use.
* @param inputStream the XML.
* @param elemNames how to load element names.
* @return the loaded tables and the names not found (and thus not loaded).
*/
public Tuple2<Set<SQLTable>, Set<String>> load(DBRoot b, final String variant, InputStream inputStream) {
public Tuple2<Set<SQLTable>, Set<String>> load(DBRoot b, final String variant, InputStream inputStream, final SQLElementNamesFromXML elemNames) {
if (inputStream == null)
throw new NullPointerException("inputStream is null");
final Set<SQLTable> res = new HashSet<SQLTable>();
259,19 → 257,19
final String elemName = elem.getName().toLowerCase();
final DBRoot root;
final List<Element> tableElems;
if (elemName.equals("table")) {
if (elemName.equals("table") || elemName.equals("element")) {
root = b;
tableElems = Collections.singletonList(elem);
} else if (elemName.equals("root")) {
root = b.getDBSystemRoot().getRoot(elem.getAttributeValue("name"));
tableElems = getChildren(elem);
} else {
if (elemName.equals("root")) {
Log.get().warning("Ignoring deprecated <root> element, use element code to refer to tables outside the default root");
}
root = null;
tableElems = null;
}
if (tableElems != null) {
for (final Element tableElem : tableElems) {
final Tuple2<String, SQLTable> t = load(root, variant, tableElem, true);
final Tuple2<String, SQLTable> t = load(root, variant, tableElem, elemNames, true);
if (t.get1() == null) {
notFound.add(t.get0());
} else {
290,11 → 288,19
return Tuple2.create(res, notFound);
}
 
private Tuple2<String, SQLTable> load(DBRoot b, final String variant, final Element tableElem, final boolean lenient) {
private Tuple2<String, SQLTable> load(DBRoot b, final String variant, final Element tableElem, final SQLElementNamesFromXML elemNames, final boolean lenient) {
final String tableName = tableElem.getAttributeValue("name");
SQLTable table = this.dir == null || this.dir.getElement(tableName) == null ? b.getTable(tableName) : this.dir.getElement(tableName).getTable();
if (table == null && this.dir != null && this.dir.getElementForCode(tableName) != null)
table = this.dir.getElementForCode(tableName).getTable();
String elemCode = tableElem.getAttributeValue("refid");
SQLTable table = null;
// compatibility mode for files without element names
final boolean compatMode = tableElem.getName().toLowerCase().equals("table");
if (compatMode) {
table = this.dir == null || this.dir.getElement(tableName) == null ? b.getTable(tableName) : this.dir.getElement(tableName).getTable();
if (elemCode == null)
elemCode = tableName;
}
if (table == null && this.dir != null && this.dir.getElementForCode(elemCode) != null)
table = this.dir.getElementForCode(elemCode).getTable();
if (table != null) {
for (final Element elem : getChildren(tableElem)) {
final String elemName = elem.getName().toLowerCase();
307,6 → 313,15
}
}
}
try {
if (!compatMode) {
final Entry<String, Phrase> phrase = elemNames.createPhrase(tableElem);
if (phrase != null)
this.putElementName(this.dir.getElement(table), phrase.getValue(), variant);
}
} catch (IOException e) {
throw new IllegalStateException("Couldn't parse phrase for " + table, e);
}
} else if (lenient) {
// allow to supply the union all tables and ignore those that aren't in a given base
Log.get().config("Ignore loading of inexistent table " + tableName);
568,4 → 583,45
}
this.removeTranslation(elemTable, componentCode, DB_VARIANT, name);
}
 
// ** element names
 
public final Phrase putElementName(final Class<? extends SQLElement> elemCl, final Phrase phr) {
return this.putElementName(this.getDirectory().getElement(elemCl), phr);
}
 
public final Phrase putElementName(final SQLElement elem, final Phrase phr) {
return this.putElementName(elem, phr, CORE_VARIANT);
}
 
public synchronized final Phrase putElementName(final SQLElement elem, final Phrase phr, final String variant) {
return this.getElementNameMap(elem, true).put(variant, phr);
}
 
private synchronized final Map<String, Phrase> getElementNameMap(final SQLElement elem, final boolean create) {
Map<String, Phrase> elemMap = this.elementNames.get(elem.getCode());
if (elemMap == null && create) {
elemMap = new HashMap<>();
this.elementNames.put(elem.getCode(), elemMap);
}
return elemMap;
}
 
public final Phrase getElementName(final SQLElement elem) {
return this.getElementName(elem, elem.getMDPath());
}
 
public final Phrase getElementName(final SQLElement elem, final List<String> variantPath) {
for (final String variant : variantPath) {
final Phrase res = this.getElementName(elem, variant);
if (res != null)
return res;
}
return this.getElementName(elem, CORE_VARIANT);
}
 
private synchronized final Phrase getElementName(final SQLElement elem, final String variant) {
final Map<String, Phrase> map = this.getElementNameMap(elem, false);
return map == null ? null : map.get(variant);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementNamesMap.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementNames.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementDirectory.java
15,7 → 15,6
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.ShowAs;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBStructureItemNotFound;
import org.openconcerto.sql.model.SQLName;
25,12 → 24,7
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.i18n.LocalizedInstances;
import org.openconcerto.utils.i18n.Phrase;
import org.openconcerto.utils.i18n.TranslationManager;
 
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
37,13 → 31,10
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
 
import org.jdom2.JDOMException;
 
import net.jcip.annotations.GuardedBy;
 
/**
53,29 → 44,6
*/
public final class SQLElementDirectory {
 
public static final String BASENAME = SQLElementNames.class.getSimpleName();
private static final LocalizedInstances<SQLElementNames> LOCALIZED_INSTANCES = new LocalizedInstances<SQLElementNames>(SQLElementNames.class, TranslationManager.getControl()) {
@Override
protected SQLElementNames createInstance(String bundleName, Locale candidate, Class<?> cl) throws IOException {
final InputStream ins = cl.getResourceAsStream('/' + getControl().toResourceName(bundleName, "xml"));
if (ins == null)
return null;
final SQLElementNamesFromXML res = new SQLElementNamesFromXML(candidate);
try {
res.load(ins);
} catch (JDOMException e) {
throw new IOException("Invalid XML", e);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e);
} finally {
ins.close();
}
return res;
}
};
 
private final Map<SQLTable, SQLElement> elements;
private final SetMap<String, SQLTable> tableNames;
private final SetMap<String, SQLTable> byCode;
83,7 → 51,6
private final List<DirectoryListener> listeners;
 
private String phrasesPkgName;
private final Map<String, SQLElementNames> elementNames;
 
@GuardedBy("this")
private SQLFieldTranslator translator;
100,7 → 67,6
this.listeners = new ArrayList<DirectoryListener>();
 
this.phrasesPkgName = null;
this.elementNames = new HashMap<String, SQLElementNames>();
 
this.showAs = new ShowAs((DBRoot) null);
}
152,17 → 118,26
* @param element the element to add.
*/
public final void addSQLElement(final Class<? extends SQLElement> element) {
this.addSQLElement(element, null);
}
 
public final void addSQLElement(final Class<? extends SQLElement> element, final DBRoot root) {
final SQLElement newInstance;
try {
this.addSQLElement(element.getConstructor().newInstance());
if (root == null)
newInstance = element.getConstructor().newInstance();
else
newInstance = element.getConstructor(DBRoot.class).newInstance(root);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof DBStructureItemNotFound) {
Log.get().config("ignore inexistent tables: " + e.getCause().getLocalizedMessage());
return;
}
throw new IllegalArgumentException("ctor failed", e);
throw new IllegalArgumentException("Constructor failed", e);
} catch (Exception e) {
throw new IllegalArgumentException("no-arg ctor failed", e);
throw new IllegalArgumentException("Couldn't use constructor", e);
}
this.addSQLElement(newInstance);
}
 
/**
293,43 → 268,6
return this.phrasesPkgName;
}
 
protected synchronized final SQLElementNames getElementNames(final String pkgName, final Locale locale, final Class<?> cl) {
if (pkgName == null)
return null;
final char sep = ' ';
final String key = pkgName + sep + locale.toString();
assert pkgName.indexOf(sep) < 0 : "ambiguous key : " + key;
SQLElementNames res = this.elementNames.get(key);
if (res == null) {
final List<SQLElementNames> l = LOCALIZED_INSTANCES.createInstances(pkgName + "." + BASENAME, locale, cl).get1();
if (!l.isEmpty()) {
for (int i = 1; i < l.size(); i++) {
l.get(i - 1).setParent(l.get(i));
}
res = l.get(0);
}
this.elementNames.put(key, res);
}
return res;
}
 
/**
* Search a name for the passed instance and the {@link TM#getTranslationsLocale() current
* locale}. Search for {@link SQLElementNames} using {@link LocalizedInstances} and
* {@link SQLElementNamesFromXML} first in {@link SQLElement#getL18nPackageName()} then in
* {@link #getL18nPackageName()}. E.g. this could load SQLElementNames_en.class and
* SQLElementNames_en_UK.xml.
*
* @param elem the element.
* @return the name if found, <code>null</code> otherwise.
*/
public final Phrase getName(final SQLElement elem) {
final String elemBaseName = elem.getL18nPackageName();
final String pkgName = elemBaseName == null ? getL18nPackageName() : elemBaseName;
final SQLElementNames elementNames = getElementNames(pkgName, TM.getInstance().getTranslationsLocale(), elem.getL18nClass());
return elementNames == null ? null : elementNames.getName(elem);
}
 
public synchronized final void addListener(DirectoryListener dl) {
this.listeners.add(dl);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SharedSQLElement.java
13,6 → 13,7
package org.openconcerto.sql.element;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLTable;
 
/**
26,10 → 27,18
super(tableName);
}
 
public SharedSQLElement(final Configuration conf, String tableName, final String code) {
super(conf, tableName, null, code);
}
 
public SharedSQLElement(SQLTable table) {
super(table);
this(table, null);
}
 
public SharedSQLElement(final SQLTable table, final String code) {
super(table, null, code);
}
 
@Override
public final boolean isShared() {
return true;
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementNamesFromXML.java
23,8 → 23,13
import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Pattern;
 
import org.jdom2.Document;
50,27 → 55,61
*
*/
@ThreadSafe
public class SQLElementNamesFromXML extends SQLElementNamesMap.ByCode {
public class SQLElementNamesFromXML {
 
static public final Pattern SPLIT_PATTERN = Pattern.compile("\\s*,\\s*");
static private final Set<Object> SHORT_VARIANTS = Collections.singleton(Grammar.PLURAL);
 
private static final String ELEMENT_ELEM_NAME = "element";
private static final String NAME_ID_ATTR = "refid";
private static final String VARIANT_ATTR = "variant";
private static final String VARIANT_REFIDS_ATTR = "refids";
private static final String VARIANT_VALUE_ATTR = "value";
private static final String NAME_PLURAL_ATTR = "namePlural";
 
static private final String getNounClassAttrName(final boolean shortForm) {
return shortForm ? "nameClass" : "nounClass";
}
 
// <element nameClass="masculine" name="contact fournisseur">
// <name nounClass="masculine" base="droit utilisateur">
static final private void setNounClassAndBase(final Element elem, final Phrase phrase, final boolean shortForm) {
elem.setAttribute(getNounClassAttrName(shortForm), phrase.getNounClass().getName());
elem.setAttribute(shortForm ? "name" : "base", phrase.getBase());
}
 
private final Locale locale;
 
public SQLElementNamesFromXML(Locale locale) {
super(locale);
this.locale = locale;
}
 
public final void load(final InputStream ins) throws JDOMException, IOException {
final Grammar gr = Grammar.getInstance(getLocale());
public final Locale getLocale() {
return this.locale;
}
 
public final Map<String, Phrase> load(final InputStream ins) throws JDOMException, IOException {
final Document doc = new SAXBuilder().build(ins);
for (final Element elem : doc.getRootElement().getChildren("element"))
this.load(gr, elem);
return load(doc.getRootElement());
}
 
public final Map<String, Phrase> load(final Element root) throws IOException {
final Grammar gr = Grammar.getInstance(getLocale());
final Map<String, Phrase> res = new HashMap<>();
for (final Element elem : root.getChildren(ELEMENT_ELEM_NAME)) {
final Entry<String, Phrase> e = this.createPhrase(gr, elem);
if (e != null)
res.put(e.getKey(), e.getValue());
}
return res;
}
 
public final Entry<String, Phrase> createPhrase(final Element elem) throws IOException {
return this.createPhrase(Grammar.getInstance(getLocale()), elem);
}
 
private Entry<String, Phrase> createPhrase(final Grammar gr, final Element elem) throws IOException {
final String refid = elem.getAttributeValue("refid");
final String refid = elem.getAttributeValue(NAME_ID_ATTR);
if (refid == null)
throw new IOException("No refid attribute");
 
78,7 → 117,8
final boolean hasChild = nameElem != null;
final String nameAttr = elem.getAttributeValue("name");
if (!hasChild && nameAttr == null) {
Log.get().warning("No name for code : " + refid);
// perhaps elem is just used to change item names
Log.get().log(Level.FINER, "No name for code : {0}", refid);
return null;
}
if (hasChild && nameAttr != null) {
88,23 → 128,23
final String base = hasChild ? nameElem.getAttributeValue("base") : nameAttr;
if (base == null)
throw new IOException("No base for the name of " + refid);
final String nounClassName = hasChild ? nameElem.getAttributeValue("nounClass") : elem.getAttributeValue("nameClass");
final String nounClassName = (hasChild ? nameElem : elem).getAttributeValue(getNounClassAttrName(!hasChild));
final NounClass nounClass = nounClassName == null ? null : gr.getNounClass(nounClassName);
 
final Phrase res = new Phrase(gr, base, nounClass);
if (!hasChild) {
// most languages have at most 2 grammatical number
final String plural = elem.getAttributeValue("namePlural");
final String plural = elem.getAttributeValue(NAME_PLURAL_ATTR);
if (plural != null)
res.putVariant(Grammar.PLURAL, plural);
} else {
for (final Element variantElem : nameElem.getChildren("variant")) {
final String value = variantElem.getAttributeValue("value");
for (final Element variantElem : nameElem.getChildren(VARIANT_ATTR)) {
final String value = variantElem.getAttributeValue(VARIANT_VALUE_ATTR);
if (value == null) {
warning(refid, variantElem, "No value");
continue;
}
final String variantIDs = variantElem.getAttributeValue("refids");
final String variantIDs = variantElem.getAttributeValue(VARIANT_REFIDS_ATTR);
final String variantPattern = variantElem.getAttributeValue("idPattern");
if (variantIDs == null && variantPattern == null) {
warning(refid, variantElem, "No ID");
134,13 → 174,35
return new SimpleEntry<String, Phrase>(refid, res);
}
 
private void load(final Grammar gr, final Element elem) throws IOException {
final Entry<String, Phrase> entry = this.createPhrase(gr, elem);
if (entry != null)
this.put(entry.getKey(), entry.getValue());
}
 
private void warning(final String refid, final Element variantElem, final String msg) {
Log.get().warning(msg + " for variant of " + refid + " : " + JDOM2Utils.output(variantElem));
}
 
public final Element createElement(final String refID, final Phrase phrase) {
final Element elem = new Element(ELEMENT_ELEM_NAME);
elem.setAttribute(NAME_ID_ATTR, refID);
final Set<VariantKey> explicitVariants = phrase.getExplicitVariants();
final boolean shortForm = SHORT_VARIANTS.containsAll(explicitVariants);
final Element nameElem;
if (shortForm) {
nameElem = elem;
} else {
nameElem = new Element("name");
elem.addContent(nameElem);
}
setNounClassAndBase(nameElem, phrase, shortForm);
if (!shortForm) {
// <variant refids="plural" value="droits utilisateurs"
for (final VariantKey explicitVariant : explicitVariants) {
final Element variantElem = new Element(VARIANT_ATTR);
variantElem.setAttribute(VARIANT_REFIDS_ATTR, explicitVariant.getID());
variantElem.setAttribute(VARIANT_VALUE_ATTR, phrase.getVariant(explicitVariant));
nameElem.addContent(variantElem);
}
} else if (explicitVariants.contains(Grammar.PLURAL)) {
elem.setAttribute(NAME_PLURAL_ATTR, phrase.getVariant(Grammar.PLURAL));
}
return elem;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElement.java
159,8 → 159,8
}
final Phrase res = new Phrase(Grammar_fr.getInstance(), base, nounClass);
if (nounClass != null)
res.putVariant(Grammar.INDEFINITE_ARTICLE_SINGULAR, singular);
res.putVariant(Grammar.PLURAL, plural);
res.putVariantIfDifferent(Grammar.INDEFINITE_ARTICLE_SINGULAR, singular);
res.putVariantIfDifferent(Grammar.PLURAL, plural);
return res;
}
 
184,9 → 184,7
 
@GuardedBy("this")
private SQLElementDirectory directory;
private String l18nPkgName;
private Class<?> l18nClass;
private Phrase name;
private Phrase defaultName;
private final SQLTable primaryTable;
// used as a key in SQLElementDirectory so it should be immutable
private String code;
233,7 → 231,6
throw new DBStructureItemNotFound("table is null for " + this.getClass());
}
this.primaryTable = primaryTable;
this.setL18nPackageName(null);
this.setDefaultName(name);
this.code = code == null ? createCode() : code;
this.combo = null;
256,7 → 253,11
 
/**
* Should return the code for this element. This method is only called if the <code>code</code>
* parameter of the constructor is <code>null</code>.
* parameter of the constructor is <code>null</code>. This implementation returns a string
* containing the {@link Class#getName() full name} of the class and the {@link #getTable()
* table} name to handle a single class being used for multiple tables. NOTE: this method is
* also needed, since a subclass constructor cannot pass <code>this.getClass().getName()</code>
* as the code parameter to <code>super</code>.
*
* @return the default code for this element.
*/
598,7 → 599,10
 
// return Path from owner to owned
private final Set<Path> createPaths(final boolean wantedOwned) {
assert !(this instanceof JoinSQLElement) : "joins cannot have SQLElementLink : " + this;
// joins cannot have SQLElementLink
if (this instanceof JoinSQLElement)
return Collections.emptySet();
 
final SQLTable thisTable = this.getTable();
final Set<Link> allLinks = thisTable.getDBSystemRoot().getGraph().getAllLinks(getTable());
final Set<Path> res = new HashSet<Path>();
754,7 → 758,11
private synchronized void initRF() {
if (this.otherLinks != null)
return;
 
final Set<Path> otherPaths = this.createPaths(false);
if (otherPaths.isEmpty()) {
this.otherLinks = SQLElementLinks.empty();
} else {
final SetMap<LinkType, SQLElementLink> tmp = new SetMap<LinkType, SQLElementLink>();
for (final Path p : otherPaths) {
final SQLElement refElem = this.getElementLenient(p.getFirst());
770,6 → 778,7
}
this.otherLinks = new SQLElementLinks(tmp);
}
}
 
final void setDirectory(final SQLElementDirectory directory) {
// since this method should only be called at the end of SQLElementDirectory.addSQLElement()
812,36 → 821,7
return this.getTable().getBase().getGraph().getForeignTable(this.getTable().getField(foreignField));
}
 
public final synchronized String getL18nPackageName() {
return this.l18nPkgName;
}
 
public final synchronized Class<?> getL18nClass() {
return this.l18nClass;
}
 
public final void setL18nLocation(Class<?> clazz) {
this.setL18nLocation(clazz.getPackage().getName(), clazz);
}
 
public final void setL18nPackageName(String name) {
this.setL18nLocation(name, null);
}
 
/**
* Set the location for the localized name.
*
* @param name a package name, can be <code>null</code> :
* {@link SQLElementDirectory#getL18nPackageName()} will be used.
* @param ctxt the class loader to load the resource, <code>null</code> meaning this class.
* @see SQLElementDirectory#getName(SQLElement)
*/
public final synchronized void setL18nLocation(final String name, final Class<?> ctxt) {
this.l18nPkgName = name;
this.l18nClass = ctxt == null ? this.getClass() : ctxt;
}
 
/**
* Set the default name, used if no translations could be found.
*
* @param name the default name, if <code>null</code> the {@link #getTable() table} name will be
848,7 → 828,7
* used.
*/
public final synchronized void setDefaultName(Phrase name) {
this.name = name != null ? name : Phrase.getInvariant(getTable().getName());
this.defaultName = name != null ? name : Phrase.getInvariant(getTable().getName());
}
 
/**
857,7 → 837,7
* @return the default name, never <code>null</code>.
*/
public final synchronized Phrase getDefaultName() {
return this.name;
return this.defaultName;
}
 
/**
869,7 → 849,8
*/
public final Phrase getName() {
final SQLElementDirectory dir = this.getDirectory();
final Phrase res = dir == null ? null : dir.getName(this);
final SQLFieldTranslator trns = dir == null ? null : dir.getTranslator();
final Phrase res = trns == null ? null : trns.getElementName(this);
return res == null ? this.getDefaultName() : res;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/element/ConfSQLElement.java
25,21 → 25,6
*/
public class ConfSQLElement extends SQLElement {
 
@Deprecated
public ConfSQLElement(String tableName, String singular, String plural) {
this(Configuration.getInstance(), tableName, singular, plural);
}
 
@Deprecated
public ConfSQLElement(Configuration conf, String tableName, String singular, String plural) {
this(conf.getRoot().findTable(tableName), singular, plural);
}
 
@Deprecated
public ConfSQLElement(SQLTable table, String singular, String plural) {
super(singular, plural, table);
}
 
public ConfSQLElement(String tableName) {
this(tableName, null);
}
48,22 → 33,30
this(conf, tableName, null);
}
 
public ConfSQLElement(SQLTable table) {
this(table, null);
}
 
public ConfSQLElement(String tableName, final Phrase name) {
this(Configuration.getInstance(), tableName, name);
}
 
public ConfSQLElement(Configuration conf, String tableName, final Phrase name) {
this(conf.getRoot().findTable(tableName), name);
this(conf, tableName, name, null);
}
 
public ConfSQLElement(Configuration conf, String tableName, final Phrase name, final String code) {
this(conf.getRoot().findTable(tableName), name, code);
}
 
public ConfSQLElement(final SQLTable table) {
this(table, null);
}
 
public ConfSQLElement(final SQLTable primaryTable, final Phrase name) {
super(primaryTable, name);
this(primaryTable, name, null);
}
 
public ConfSQLElement(final SQLTable primaryTable, final Phrase name, final String code) {
super(primaryTable, name, code);
}
 
@Override
protected List<String> getComboFields() {
return Collections.emptyList();
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/SQLElementNames_fr.xml
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/SQLElementNames_en.xml
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_en.properties
69,7 → 69,18
editFrame.look=Details of {element__singularIndefiniteArticle}
 
listPanel.cloneToolTip=<html>Allow to duplicate a row.<br>Hold CTRL down to also duplicate the content<br>Hold Shift down to change the location.</html>
listPanel.cloneRows=Do you want to clone {rowCount, plural, one {this row{rec, select, true { and its content} other {}}} other {these # rows{rec, select, true { and their contents} other {}}}} ?
listPanel.cloneRows=Do you want to clone {rec, select,\
true {\
{rowCount, plural,\
one {this row and its content ?}\
other {these # rows and their content ?}\
}}\
other {\
{rowCount, plural,\
one {this row ?}\
other {these # rows ?}\
}}\
}
listPanel.noSelectionOrSort=No selection or list sorted
listPanel.export=List export
listPanel.save=Save the list
87,8 → 98,10
ilist.unlockRows=Unlock rows
ilist.metadata={0,choice,0#Modified|1#Created}{1,choice,0#|1# by {2} {3}}{4,choice,0#|1#, {5,date,long} at {5,time,medium}}
 
sqlComp.stringValueTooLong=The value is {0} character{0,choice,1#|1<s} too long
sqlComp.bdTooHigh=Number too high, it must have at most {0} digit{0,choice,1#|1<s} before the decimal point ({1} after)
sqlComp.stringValueTooLong=The value is {0, plural, one {# character too long} other {# characters too long}}
sqlComp.bdTooHigh=Number too high, {0, plural,\
one {it must have at most # digit before the decimal point ({1} after)}\
other {it must have at most # digits before the decimal point ({1} after)}}
sqlComp.invalidItem={0} is invalid{1,choice,0#|1# ({2})}
sqlComp.emptyItem={0} is empty
sqlComp.insertError=Error while inserting
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_es.properties
New file
0,0 → 1,199
init.error=Error de inicialización
add=Añadir
saveModifications=Guardar cambios
display=Ver
modify=Editar
delete=Limpiar
remove=Borrar
close=Cerrar
cancel=Cancelar
search=Buscar
open=Abrir
save=Guardar
backup=Hacer la copia de seguridad
export=Exportar
noSelection=Sin selección
duplicate=Duplicar
duplication=Duplicación
location=Ubicación
choose=Elige
toApply=Aplicar
 
all=Todo
toReverse=Revertir
 
contains=Contiene
contains.exactly=Contiene exactamente
isLessThan=Es menos de
isLessThanOrEqualTo=Es menor o igual a
isEqualTo=Es igual a
isExactlyEqualTo=Es exactamente igual a
isGreaterThan=Es más de
isGreaterThanOrEqualTo=Es más o igual a
isEmpty=Esta vacio
 
clone.newPlace=Nueva ubicación (opcional) :
 
saveError=Error al guardar
 
loginPanel.storePass=Almacenar contraseña
loginPanel.loginAction=Iniciar sesión
loginPanel.adminLogin=Administrador
loginPanel.loginLabel=Nombre de usuario
loginPanel.passLabel=Contraseña
loginPanel.companyLabel=Empresa
loginPanel.disabledUser=Cuenta de usuario deshabilitada
loginPanel.unknownUser=Usuario desconocido
loginPanel.multipleUser=Múltiples usuarios nombrados "{0}"
loginPanel.wrongPass=Contraseña incorrecta
 
noRightToAdd=No puedes agregar
noRightToModify=No puedes editar
noRightToDel=No puedes borrar
noRightToClone=No puedes duplicar
noRightToReorder=No puedes cambiar la orden
 
 
editPanel.keepOpen=No cerrar ventana
editPanel.readOnlySelection=This row is read only
editPanel.localPrivateSelection=This row is used by another item
editPanel.inexistentElement=this item doesn't exist
editPanel.cancelError=Error while canceling
editPanel.modifyError=Error while modifying
editPanel.addError=Error while adding
editPanel.deleteError=Error while deleting
editPanel.invalidContent=Input fields aren''t filled correctly.\n\nYou cannot save modifications :
editPanel.invalidContent.unknownReason= they aren''t valid
 
editAction.name=Crear {element__singularIndefiniteArticle}
editFrame.create=Crear {element__singularIndefiniteArticle}
editFrame.modify=Modificar {element__singularIndefiniteArticle}
editFrame.look=Detalles de {element__singularIndefiniteArticle}
 
listPanel.cloneToolTip=<html>Allow to duplicate a row.<br>Hold CTRL down to also duplicate the content<br>Hold Shift down to change the location.</html>
listPanel.cloneRows=listPanel.cloneRows=Do you want to clone {rec, select,\
true {\
{rowCount, plural,\
one {this row and its content ?}\
other {these # rows and their content ?}\
}}\
other {\
{rowCount, plural,\
one {this row ?}\
other {these # rows ?}\
}}\
}
listPanel.clone.collectingData=Collecting data for {0,plural,one{# row} other{# rows}}\u2026
listPanel.clone.storingData=Storing {0,plural,one{# row} other{# rows}}\u2026
listPanel.noSelectionOrSort=No selection or list sorted
listPanel.export=List export
listPanel.save=Save the list
listPanel.wholeList=the whole list
listPanel.selection=the selection
listPanel.duplicationError=Couldn''t duplicate {0}
 
listAction.name=Manage {element__pluralDefiniteArticle}
element.list=Lista de {element__plural}
 
ilist.setColumnsWidth=Ajustar ancho columnas
ilist.showAllColumns=Ver todas las columnas
ilist.lastCol=Cannot hide the last column
ilist.lockRows=Lock rows
ilist.unlockRows=Unlock rows
ilist.metadata={0,choice,0#Modified|1#Created}{1,choice,0#|1# by {2} {3}}{4,choice,0#|1#, {5,date,long} at {5,time,medium}}
 
sqlComp.stringValueTooLong=The value is {0, plural, one {# character too long} other {# characters too long}}
sqlComp.bdTooHigh=Number too high, {0, plural,\
one {it must have at most # digit before the decimal point ({1} after)}\
other {it must have at most # digits before the decimal point ({1} after)}}
sqlComp.invalidItem={0} is invalid{1,choice,0#|1# ({2})}
sqlComp.emptyItem={0} is empty
sqlComp.insertError=Error while inserting
sqlComp.insertCancelled=Insertion cancelled
sqlComp.selectError=Error while displaying {0}
sqlComp.updateError=Error while updating
sqlComp.updateCancelled=Update cancelled
sqlComp.updateCancelled.archived=The row was archived
sqlComp.updateCancelled.moreArchivedRows=The database was modified (there are more rows to be archived)
sqlComp.rowsToArchiveError=Error while searching data to archive {0}
sqlComp.archiveError=Error while archiving
sqlComp.archiveCancelled=Archive cancelled
sqlComp.saveDocError=Error while saving documentation of {0}
sqlComp.modifyDoc=Modify the documentation
sqlComp.deletedRow=The row is no longer in the database : {0}
 
sqlElement.archive.computingRows=Computing rows to archive\u2026
sqlElement.archiveError=Error while archiving {0} IDs {1}
sqlElement.confirmDelete=Confirm deletion
sqlElement.deleteNoRef=Do you want to delete {rowCount, plural, one {this row} other {these # rows}} ?
sqlElement.deleteRef.details= {descsSize, plural, =0 {} other\
{{rowCount, plural, one {This row is used} other {These rows are used}} by :\n\
{descs}}}\
{externsSize, plural, =0 {} other {{descsSize, plural, =0 {The} other {\n\nFurther the}} following links will be IRRETRIEVABLY cut : \n\
{externs}}}\n\n
sqlElement.deleteRef.details2=The following links will be IRRETRIEVABLY cut, they couldn\u2019t be 'unarchived' :\n\
{externs}\n\n
sqlElement.deleteRef=Do you{times, select, once {} other { REALLY}} want to delete {rowCount, plural, one {this row} other {these # rows}} and all linked ones ?
sqlElement.noLinesDeleted=No lines deleted.
sqlElement.noLinesDeletedTitle=Information
sqlElement.linksWillBeCut=- {elementName__indefiniteNumeral} {count, plural, one {will lose its} other {will lose their}} "{linkName}"
sqlElement.linkCantBeCut={rowDesc} cannot lose its field "{fieldLabel}"
 
sqlElement.deletePrivateNoRef={elementName__indefiniteNumeral} {elementNameCount, plural, one {is to be deleted} other {are to be deleted}}.
 
sqlElement.deletePrivateRef.details=The removal of {elementName__indefiniteNumeral} will cause :\n\
{descsSize, plural, =0 {} other {{descs}\n}}\
{externs}
sqlElement.deletePrivateRef.desc=- the removal of {elementName__indefiniteNumeral}
sqlElement.deletePrivateRef.linksWillBeCut=- the permanent deletion of the field "{linkName}" for {elementName__indefiniteNumeral}
 
sqlElement.modify.deletePrivate=\n\nModify {elementName__singularDemonstrative} and delete the informations listed above ?
 
user.passwordsDontMatch=Passwords don\u2019t match
user.passwordsDontMatch.short=Passwords don\u2019t match
 
infoPanel.rights=Rights enabled
infoPanel.appName=Application name
infoPanel.noAppName=unknown
infoPanel.version=Application version
infoPanel.noVersion=unknown
infoPanel.secureLink=Secure link
infoPanel.dbURL=Database URL
infoPanel.dirs=Folders
infoPanel.logs=Logs
infoPanel.docs=Documents
infoPanel.dataDir=Data
infoPanel.prefsDir=Preferences
infoPanel.cacheDir=Cache
 
infoPanel.softwareTitle=Software
infoPanel.systemTitle=System information
infoPanel.refresh=Click to refresh
 
backupPanel.backup=Backup
backupPanel.createFolderError=Couldn't create destination folder. Backup canceled !
backupPanel.folderRightsError=Insufficient rights on destination folder. Backup canceled !
backupPanel.errorsOnLastBackup=Errors occurred on last backup. Please contact IT.
backupPanel.lastBackup=Last backup {date, date, long} at {date, time, short}\non {destination}
backupPanel.differentDisks=Please backup on different disks !
backupPanel.progress=Backup progress
backupPanel.inProgress=Backup in progress
backupPanel.closingIn=Closing in {0}s
backupPanel.endFail=Backup ended with errors !
backupPanel.endSuccess=Backup ended successfully
backupPanel.failed=Backup failed
 
rights=Rights
rightsPanel.defaultRights=Default rights
rights.allTables=All tables
 
combo.list=List {element__pluralDefiniteArticle}
 
joinComp.usedBy=Cannot delete selection, it is used by "{0}"
 
browserCol.content=Content of {element__pluralDefiniteArticle}
 
addNewLine=Añadir nueva línea
insertNewLine=Insertar línea
duplicateLine=Duplicar líneas seleccionadas
deleteLine=Eliminar líneas seleccionadas
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_fr.properties
69,8 → 69,18
editFrame.look=Détail {element__de__singularIndefiniteArticle}
 
listPanel.cloneToolTip=<html>Permet de dupliquer une ligne.<br>Maintenir CTRL enfoncé pour dupliquer également le contenu<br>Maintenir Maj. enfoncé pour changer d'emplacement.</html>
listPanel.cloneRows=Voulez-vous cloner {rowCount, plural, one {cette ligne{rec, select, true { et son contenu} other {}}}\
other {ces # lignes{rec, select, true { et leurs contenus} other {}}}} ?
listPanel.cloneRows=Voulez-vous cloner {rec, select,\
true {\
{rowCount, plural,\
one {cette ligne et son contenu ?}\
other {ces # lignes et leurs contenus ?}\
}}\
other {\
{rowCount, plural,\
one {cette ligne ?}\
other {ces # lignes ?}\
}}\
}
listPanel.noSelectionOrSort=Pas de sélection ou liste triée
listPanel.export=Export de la liste
listPanel.save=Sauver la liste
88,8 → 98,10
ilist.unlockRows=Déverrouiller les lignes
ilist.metadata={0,choice,0#Modifiée|1#Créée}{1,choice,0#|1# par {2} {3}}{4,choice,0#|1# le {5,date,long} à {5,time,medium}}
 
sqlComp.stringValueTooLong=La valeur fait {0} caractère{0,choice,1#|1<s} de trop
sqlComp.bdTooHigh=Nombre trop grand, il doit faire moins de {0} chiffre{0,choice,1#|1<s} avant la virgule ({1} après)
sqlComp.stringValueTooLong=La valeur fait {0, plural, one {# caractère de trop} other {# caractères de trop}}
sqlComp.bdTooHigh=Nombre trop grand, {0, plural,\
one {il doit faire moins d\u2019un chiffre avant la virgule ({1} après)}\
other {il doit faire moins de # chiffres avant la virgule ({1} après)}}
sqlComp.invalidItem={0} n''est pas valide{1,choice,0#|1# ({2})}
sqlComp.emptyItem={0} est vide
sqlComp.insertError=Impossible d''insérer
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_pl.properties
New file
0,0 → 1,198
init.error=Initialization error
add=Add
saveModifications=Save modifications
display=Display
modify=Modify
delete=Delete
remove=Remove
close=Close
cancel=Cancel
search=Search
open=Open
save=Save
backup=Backup
export=Export
noSelection=No selection
duplicate=Duplicate
duplication=Duplication
location=Location
choose=Choose
toApply=Apply
 
all=All
toReverse=Reverse
 
contains=Contains
contains.exactly=Contains exactly
isLessThan=Is less than
isLessThanOrEqualTo=Is less than or equal to
isEqualTo=Is equal to
isExactlyEqualTo=Is exactly equal to
isGreaterThan=Is greater than
isGreaterThanOrEqualTo=Is greater than or equal to
isEmpty=Is empty
 
clone.newPlace=New location (optional) :
 
saveError=Error while saving
 
loginPanel.storePass=Store password
loginPanel.loginAction=Log in
loginPanel.adminLogin=Administrator
loginPanel.loginLabel=Login
loginPanel.passLabel=Password
loginPanel.companyLabel=Company
loginPanel.disabledUser=Disabled user account
loginPanel.unknownUser=Unknown user
loginPanel.multipleUser=Multiple users named "{0}"
loginPanel.wrongPass=Wrong password
 
noRightToAdd=You''re not allowed to add
noRightToModify=You''re not allowed to modify
noRightToDel=You''re not allowed to delete
noRightToClone=You''re not allowed to duplicate
noRightToReorder=You''re not allowed to change order
 
editPanel.keepOpen=don''t close the window
editPanel.readOnlySelection=This row is read only
editPanel.localPrivateSelection=This row is used by another item
editPanel.inexistentElement=this item doesn't exist
editPanel.cancelError=Error while canceling
editPanel.modifyError=Error while modifying
editPanel.addError=Error while adding
editPanel.deleteError=Error while deleting
editPanel.invalidContent=Input fields aren''t filled correctly.\n\nYou cannot save modifications :
editPanel.invalidContent.unknownReason= they aren''t valid
 
editAction.name=Create {element__singularIndefiniteArticle}
editFrame.create=Create {element__singularIndefiniteArticle}
editFrame.modify=Modify {element__singularIndefiniteArticle}
editFrame.look=Details of {element__singularIndefiniteArticle}
 
listPanel.cloneToolTip=<html>Allow to duplicate a row.<br>Hold CTRL down to also duplicate the content<br>Hold Shift down to change the location.</html>
listPanel.cloneRows=Do you want to clone {rec, select,\
true {\
{rowCount, plural,\
one {this row and its content ?}\
other {these # rows and their content ?}\
}}\
other {\
{rowCount, plural,\
one {this row ?}\
other {these # rows ?}\
}}\
}
listPanel.clone.collectingData=Collecting data for {0,plural,one{# row} other{# rows}}\u2026
listPanel.clone.storingData=Storing {0,plural,one{# row} other{# rows}}\u2026
listPanel.noSelectionOrSort=No selection or list sorted
listPanel.export=List export
listPanel.save=Save the list
listPanel.wholeList=the whole list
listPanel.selection=the selection
listPanel.duplicationError=Couldn''t duplicate {0}
 
listAction.name=Manage {element__pluralDefiniteArticle}
element.list=List of {element__plural}
 
ilist.setColumnsWidth=Adjust columns widths
ilist.showAllColumns=Show all columns
ilist.lastCol=Cannot hide the last column
ilist.lockRows=Lock rows
ilist.unlockRows=Unlock rows
ilist.metadata={0,choice,0#Modified|1#Created}{1,choice,0#|1# by {2} {3}}{4,choice,0#|1#, {5,date,long} at {5,time,medium}}
 
sqlComp.stringValueTooLong=The value is {0, plural, one {# character too long} other {# characters too long}}
sqlComp.bdTooHigh=Number too high, {0, plural,\
one {it must have at most # digit before the decimal point ({1} after)}\
other {it must have at most # digits before the decimal point ({1} after)}}
sqlComp.invalidItem={0} is invalid{1,choice,0#|1# ({2})}
sqlComp.emptyItem={0} is empty
sqlComp.insertError=Error while inserting
sqlComp.insertCancelled=Insertion cancelled
sqlComp.selectError=Error while displaying {0}
sqlComp.updateError=Error while updating
sqlComp.updateCancelled=Update cancelled
sqlComp.updateCancelled.archived=The row was archived
sqlComp.updateCancelled.moreArchivedRows=The database was modified (there are more rows to be archived)
sqlComp.rowsToArchiveError=Error while searching data to archive {0}
sqlComp.archiveError=Error while archiving
sqlComp.archiveCancelled=Archive cancelled
sqlComp.saveDocError=Error while saving documentation of {0}
sqlComp.modifyDoc=Modify the documentation
sqlComp.deletedRow=The row is no longer in the database : {0}
 
sqlElement.archive.computingRows=Computing rows to archive\u2026
sqlElement.archiveError=Error while archiving {0} IDs {1}
sqlElement.confirmDelete=Confirm deletion
sqlElement.deleteNoRef=Do you want to delete {rowCount, plural, one {this row} other {these # rows}} ?
sqlElement.deleteRef.details= {descsSize, plural, =0 {} other\
{{rowCount, plural, one {This row is used} other {These rows are used}} by :\n\
{descs}}}\
{externsSize, plural, =0 {} other {{descsSize, plural, =0 {The} other {\n\nFurther the}} following links will be IRRETRIEVABLY cut : \n\
{externs}}}\n\n
sqlElement.deleteRef.details2=The following links will be IRRETRIEVABLY cut, they couldn\u2019t be 'unarchived' :\n\
{externs}\n\n
sqlElement.deleteRef=Do you{times, select, once {} other { REALLY}} want to delete {rowCount, plural, one {this row} other {these # rows}} and all linked ones ?
sqlElement.noLinesDeleted=No lines deleted.
sqlElement.noLinesDeletedTitle=Information
sqlElement.linksWillBeCut=- {elementName__indefiniteNumeral} {count, plural, one {will lose its} other {will lose their}} "{linkName}"
sqlElement.linkCantBeCut={rowDesc} cannot lose its field "{fieldLabel}"
 
sqlElement.deletePrivateNoRef={elementName__indefiniteNumeral} {elementNameCount, plural, one {is to be deleted} other {are to be deleted}}.
 
sqlElement.deletePrivateRef.details=The removal of {elementName__indefiniteNumeral} will cause :\n\
{descsSize, plural, =0 {} other {{descs}\n}}\
{externs}
sqlElement.deletePrivateRef.desc=- the removal of {elementName__indefiniteNumeral}
sqlElement.deletePrivateRef.linksWillBeCut=- the permanent deletion of the field "{linkName}" for {elementName__indefiniteNumeral}
 
sqlElement.modify.deletePrivate=\n\nModify {elementName__singularDemonstrative} and delete the informations listed above ?
 
user.passwordsDontMatch=Passwords don\u2019t match
user.passwordsDontMatch.short=Passwords don\u2019t match
 
infoPanel.rights=Rights enabled
infoPanel.appName=Application name
infoPanel.noAppName=unknown
infoPanel.version=Application version
infoPanel.noVersion=unknown
infoPanel.secureLink=Secure link
infoPanel.dbURL=Database URL
infoPanel.dirs=Folders
infoPanel.logs=Logs
infoPanel.docs=Documents
infoPanel.dataDir=Data
infoPanel.prefsDir=Preferences
infoPanel.cacheDir=Cache
 
infoPanel.softwareTitle=Software
infoPanel.systemTitle=System information
infoPanel.refresh=Click to refresh
 
backupPanel.backup=Backup
backupPanel.createFolderError=Couldn't create destination folder. Backup canceled !
backupPanel.folderRightsError=Insufficient rights on destination folder. Backup canceled !
backupPanel.errorsOnLastBackup=Errors occurred on last backup. Please contact IT.
backupPanel.lastBackup=Last backup {date, date, long} at {date, time, short}\non {destination}
backupPanel.differentDisks=Please backup on different disks !
backupPanel.progress=Backup progress
backupPanel.inProgress=Backup in progress
backupPanel.closingIn=Closing in {0}s
backupPanel.endFail=Backup ended with errors !
backupPanel.endSuccess=Backup ended successfully
backupPanel.failed=Backup failed
 
rights=Rights
rightsPanel.defaultRights=Default rights
rights.allTables=All tables
 
combo.list=List {element__pluralDefiniteArticle}
 
joinComp.usedBy=Cannot delete selection, it is used by "{0}"
 
browserCol.content=Content of {element__pluralDefiniteArticle}
 
addNewLine=Add a new line
insertNewLine=Insert a line
duplicateLine=Duplicate selected lines
deleteLine=Remove selected lines
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/ReOrder.java
13,11 → 13,13
package org.openconcerto.sql.utils;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.FieldRef;
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLSystem;
24,6 → 26,9
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple2.List2;
import org.openconcerto.utils.convertor.NumberConvertor;
 
import java.math.BigDecimal;
30,9 → 35,13
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
 
import net.jcip.annotations.GuardedBy;
 
/**
* Reorder some or all rows of a table.
*
40,6 → 49,161
*/
public abstract class ReOrder {
 
@GuardedBy("this")
private static boolean AUTO_FIX_NULLS = false;
 
public static synchronized void setAutoFixNulls(boolean b) {
AUTO_FIX_NULLS = b;
}
 
public static synchronized boolean isAutoFixNulls() {
return AUTO_FIX_NULLS;
}
 
public static BigDecimal makeRoom(final SQLTable t, final BigDecimal roomNeeded, final boolean after, final BigDecimal destOrder) throws SQLException {
return makeRoom(t, roomNeeded, after, destOrder, 100);
}
 
/**
* Make sure that there's no rows with order in the passed range. This method accomplishes this
* by re-ordering an increasing number of rows. This method only changes orders greater than or
* equal to <code>destOrder</code> and the first row re-ordered (<code>destOrder</code> if
* <code>!after</code> the next one otherwise) always has <code>destOrder + roomNeeded</code> as
* order :
*
* <pre>
* "row foo" 1.0
* "row bar" 2.0
* "row baz" 3.0
* "row qux" 4.0
* If <code>roomNeeded</code> is 2 after order 2.0, then the new values will be :
* "row foo" 1.0
* "row bar" 2.0
* "row baz" 4.0
* "row qux" 5.0
* If on the other hand, one wants the room before 2.0, then :
* "row foo" 1.0
* "row bar" 4.0
* "row baz" 5.0
* "row qux" 6.0
* </pre>
*
* @param t the table.
* @param roomNeeded the size of the requested free range, e.g 10.
* @param after <code>true</code> if the free range should begin after <code>destOrder</code>,
* <code>false</code> if it should end before <code>destOrder</code>.
* @param destOrder the start or end of the range.
* @param initialCount the initial size of the range to re-order if there's no room.
* @return the smallest possibly used order <code>>=</code> destOrder.
* @throws SQLException if an error occurs.
*/
public static BigDecimal makeRoom(final SQLTable t, final BigDecimal roomNeeded, final boolean after, final BigDecimal destOrder, final int initialCount) throws SQLException {
if (roomNeeded.signum() <= 0)
throw new IllegalArgumentException("Negative roomNeeded");
if (initialCount < 1)
throw new IllegalArgumentException("Initial count too small");
final BigDecimal newFirst = destOrder.add(roomNeeded);
// reorder to squeeze rows upwards
// since we keep increasing count, we will eventually reorder all rows afterwards
// NOTE since we only go in one direction (from destOrder and upwards), there shouldn't be
// any DB deadlocks
int count = Math.max(initialCount, roomNeeded.intValue() + 1);
final int tableRowCount = t.getRowCount();
boolean reordered = false;
while (!reordered) {
// only push destRow upwards if we want to add before
reordered = ReOrder.create(t, destOrder, !after, count, newFirst).exec();
if (!reordered && count > tableRowCount)
throw new IllegalStateException("Unable to reorder " + count + " rows in " + t);
count *= 10;
}
return after ? destOrder : newFirst;
}
 
/**
* Get a number of free order values after/before the passed row,
* {@link #makeRoom(SQLTable, BigDecimal, boolean, BigDecimal, int) making room} if needed.
*
* @param rowCount the number of order values needed.
* @param after <code>true</code> if the free values should begin after <code>r</code>,
* <code>false</code> if they should end before <code>r</code>.
* @param r the row that is before or after the returned orders.
* @return a list of <code>rowCount</code> free orders and the new order for the passed row
* (only changed if there was not enough free values).
* @throws SQLException if an error occurs.
*/
public static Tuple2<List<BigDecimal>, BigDecimal> getFreeOrderValuesFor(final int rowCount, final boolean after, final SQLRow r) throws SQLException {
return getFreeOrderValuesFor(rowCount, after, r, isAutoFixNulls());
}
 
public static Tuple2<List<BigDecimal>, BigDecimal> getFreeOrderValuesFor(final int rowCount, final boolean after, final SQLRow r, final boolean autoFixNulls) throws SQLException {
if (rowCount == 0)
return Tuple2.<List<BigDecimal>, BigDecimal> create(Collections.<BigDecimal> emptyList(), null);
// both rows are locked FOR UPDATE, so there shouldn't be any row that can get between them
// in this transaction, as the only way to do that is to call fetchThisAndSequentialRow()
List2<SQLRow> seqRows = r.fetchThisAndSequentialRow(after);
if (seqRows == null)
throw new IllegalStateException("Couldn't find " + r);
assert seqRows.get0().equals(r) : "fetchThisAndSequentialRow() failed";
if (seqRows.get0().getOrder() == null) {
if (autoFixNulls)
Log.get().log(Level.WARNING, "Re-order table with null orders : " + r);
else
throw new IllegalStateException("Row with null order : " + r);
if (!ReOrder.create(r.getTable()).exec())
throw new IllegalStateException("Couldn't re-order table with null orders : " + r.getTable());
seqRows = r.fetchThisAndSequentialRow(after);
if (seqRows == null || seqRows.get0().getOrder() == null)
throw new IllegalStateException("Re-order table with null orders failed : " + seqRows);
}
final BigDecimal destOrder = seqRows.get0().getOrder();
if (destOrder.compareTo(ReOrder.MIN_ORDER) < 0)
throw new IllegalStateException(seqRows.get0() + " has invalid order : " + destOrder);
BigDecimal newRowOrder = destOrder;
final SQLRow otherRow = seqRows.get1();
final BigDecimal inc;
BigDecimal newOrder;
if (after && otherRow == null) {
// dernière ligne de la table
inc = ReOrder.DISTANCE;
newOrder = destOrder.add(inc);
} else {
final BigDecimal otherOrder;
if (otherRow != null) {
otherOrder = otherRow.getOrder();
} else {
// première ligne
otherOrder = ReOrder.MIN_ORDER;
}
if (otherOrder.compareTo(ReOrder.MIN_ORDER) < 0)
throw new IllegalStateException(otherRow + " has invalid order : " + otherOrder);
 
// ULP * 10 to give a little breathing room
final BigDecimal minDistance = r.getTable().getOrderULP().scaleByPowerOfTen(1);
final BigDecimal places = BigDecimal.valueOf(rowCount + 1);
// the minimum room to fit rowCount
final BigDecimal roomNeeded = minDistance.multiply(places);
final BigDecimal roomAvailable = otherOrder.subtract(destOrder).abs();
 
if (roomAvailable.compareTo(roomNeeded) < 0) {
newRowOrder = makeRoom(r.getTable(), roomNeeded, after, destOrder);
inc = minDistance;
newOrder = after ? destOrder.add(inc) : destOrder;
} else {
inc = roomAvailable.divide(places, DecimalUtils.HIGH_PRECISION);
newOrder = (after ? destOrder : otherOrder).add(inc);
}
}
assert inc.signum() > 0;
final List<BigDecimal> orders = new ArrayList<>(rowCount);
for (int i = 0; i < rowCount; i++) {
orders.add(DecimalUtils.round(newOrder, r.getTable().getOrderDecimalDigits()));
newOrder = newOrder.add(inc);
}
assert after && newRowOrder.compareTo(orders.get(0)) < 0 || !after && orders.get(rowCount - 1).compareTo(newRowOrder) < 0;
return Tuple2.create(orders, newRowOrder);
}
 
// must be zero so that we can work on negative numbers without breaking the unique constraint
public static final BigDecimal MIN_ORDER = BigDecimal.ZERO;
// preferred distance
123,15 → 287,16
 
// MAYBE return affected IDs
public final boolean exec() throws SQLException {
final UpdateBuilder updateUndef = new UpdateBuilder(this.t).setObject(this.t.getOrderField(), MIN_ORDER);
updateUndef.setWhere(new Where(this.t.getKey(), "=", this.t.getUndefinedID()));
return (Boolean) SQLUtils.executeAtomic(this.t.getBase().getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() {
final SQLTable t = this.t;
return SQLUtils.executeAtomic(this.t.getBase().getDataSource(), new ConnectionHandlerNoSetup<Boolean, SQLException>() {
@Override
public Object handle(SQLDataSource ds) throws SQLException, SQLException {
public Boolean handle(SQLDataSource ds) throws SQLException, SQLException {
final Connection conn = ds.getConnection();
final Statement stmt = conn.createStatement();
if (isAll()) {
// reorder all, undef must be at 0
if (isAll() && t.getUndefinedIDNumber() != null) {
final UpdateBuilder updateUndef = new UpdateBuilder(t).setObject(t.getOrderField(), MIN_ORDER);
updateUndef.setWhere(new Where(t.getKey(), "=", t.getUndefinedID()));
stmt.execute(updateUndef.asString());
}
stmt.execute("SELECT " + ReOrder.this.spec.getInc());
138,13 → 303,13
final BigDecimal inc = NumberConvertor.toBigDecimal((Number) SQLDataSource.SCALAR_HANDLER.handle(stmt.getResultSet()));
// needed since the cast in getInc() rounds so if the real increment is 0.006 it
// might get rounded to 0.01 and thus the last rows will overlap non moved rows
if (inc.compareTo(ReOrder.this.t.getOrderULP().scaleByPowerOfTen(1)) < 0)
if (inc.compareTo(t.getOrderULP().scaleByPowerOfTen(1)) < 0)
return false;
for (final String s : getSQL(conn, inc)) {
stmt.execute(s);
}
// MAYBE fire only changed IDs
ReOrder.this.t.fireTableModified(-1, Collections.singletonList(ReOrder.this.t.getOrderField().getName()));
t.fireTableModified(SQLRow.NONEXISTANT_ID, Collections.singletonList(t.getOrderField().getName()));
return true;
}
});
174,7 → 339,7
if (first.compareTo(MIN_ORDER) <= 0) {
this.firstToReorder = MIN_ORDER;
this.firstToReorderInclusive = false;
// make some room before the first non MIN_ORDER row so that another on can came
// make some room before the first non MIN_ORDER row so that another one can came
// before it
this.first = MIN_ORDER.add(DISTANCE).max(newFirst);
// try to keep asked value
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxPG.java
107,7 → 107,9
this.typeNames.addAll(java.sql.Date.class, "date");
this.typeNames.addAll(java.sql.Time.class, "time", "time without time zone");
this.typeNames.addAll(Blob.class, "bytea");
this.typeNames.addAll(Clob.class, "varchar", "char", "character varying", "character", "text");
// even though PG treats all Character Types equally, unbounded varchar is not standard, so
// prefer "text" which is supported by all systems
this.typeNames.addAll(Clob.class, "text", "varchar", "char", "character varying", "character");
this.typeNames.addAll(String.class, "varchar", "char", "character varying", "character", "text");
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntax.java
710,8 → 710,9
sqlType = type + "(" + size + ")";
} else {
Log.get().warning("Unbounded varchar for " + f.getSQLName());
if (this.getSystem() == SQLSystem.MYSQL)
throw new IllegalStateException("MySQL doesn't support unbounded varchar and might truncate data if reducing size of " + f.getSQLName());
// if (this.getSystem() == SQLSystem.MYSQL)
// throw new IllegalStateException("MySQL doesn't support unbounded varchar and
// might truncate data if reducing size of " + f.getSQLName());
// don't specify size
sqlType = type;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTable.java
872,11 → 872,38
*
* @return the key, <code>null</code> for a simple field.
*/
public final SQLKey getKey() {
public final SQLKey getRawKey() {
return this.key;
}
 
/**
* The key for this group.
*
* @return the key, never <code>null</code>.
* @throws IllegalStateException for a simple field.
*/
public final SQLKey getKey() throws IllegalStateException {
if (this.key == null)
throw new IllegalStateException("Not a key : " + this);
return this.key;
}
 
/**
* The foreign link for this group.
*
* @return the foreign link, never <code>null</code>.
* @throws IllegalStateException for a simple field or a primary key.
*/
public final Link getForeignLink() throws IllegalStateException {
if (this.key != null) {
final Link foreignLink = this.key.getForeignLink();
if (foreignLink != null)
return foreignLink;
}
throw new IllegalStateException("Not a foreign key : " + this);
}
 
/**
* The one and only field of this group.
*
* @return the only field of this group, only <code>null</code> if this group is a
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRow.java
23,12 → 23,10
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.utils.ReOrder;
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.Tuple2.List2;
 
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
298,10 → 296,26
this.fetchValues(true);
}
 
/**
* Fetch up-to-date values from the DB.
*
* @param useCache <code>true</code> to use the {@link SQLDataSource#isCacheEnabled() cache}.
* @return this.
*/
public final SQLRow fetchValues(final boolean useCache) {
return this.fetchValues(useCache, useCache);
}
 
/**
* Return a new instance with up-to-date values.
*
* @param useCache <code>true</code> to use the {@link SQLDataSource#isCacheEnabled() cache}.
* @return a new instance.
*/
public final SQLRow fetchNew(final boolean useCache) {
return new SQLRow(this.getTable(), this.getIDNumber()).fetchValues(useCache);
}
 
@SuppressWarnings("unchecked")
SQLRow fetchValues(final boolean readCache, final boolean writeCache) {
final IResultSetHandler handler = new IResultSetHandler(SQLDataSource.MAP_HANDLER, readCache, writeCache) {
397,14 → 411,44
return this.getValues().get(field);
}
 
public final SQLRow getRow(boolean after) {
/**
* Fetch from the DB this row and the next/previous one. ATTN the rows are locked
* {@link LockStrength#UPDATE for update}, but if this method is not called from within a
* transaction, they will immediately be obsolete.
*
* @param after <code>true</code> to return the next row, <code>false</code> to return the
* previous.
* @return {@link List2#get0() this row} and the next/previous one with only
* {@link SQLTable#getOrderField()} and {@link SQLTable#getArchiveField()} fetched,
* <code>null</code> if this row doesn't exist, the {@link List2#get1() next/previous
* row} is <code>null</code> if this is the last/first row of the table or has
* <code>null</code> order.
* @throws IllegalStateException if this is the {@link #isUndefined() undefined} row.
*/
public final List2<SQLRow> fetchThisAndSequentialRow(boolean after) throws IllegalStateException {
if (this.isUndefined())
throw new IllegalStateException("Cannot order against the undefined");
final SQLTable t = this.getTable();
final BigDecimal destOrder = this.getOrder();
final int diff = (!after) ? -1 : 1;
 
// this is one statement (subquery included) and thus atomic : the inner FOR UPDATE ensures
// that the ORDER doesn't change by the time the outer query is executed
// SELECT * FROM "test"."BATIMENT"
// WHERE "ORDRE" >= (SELECT "ORDRE" FROM "test"."BATIMENT" WHERE "ID" = 3 FOR UPDATE)
// ORDER BY "ORDRE"
// LIMIT 2
// FOR UPDATE;
 
final SQLSelect selOrder = new SQLSelect();
// OK to order against an archived
selOrder.setArchivedPolicy(SQLSelect.BOTH);
selOrder.addSelect(t.getOrderField());
selOrder.setWhere(this.getWhere());
selOrder.setLockStrength(LockStrength.UPDATE);
 
final SQLSelect sel = new SQLSelect();
// undefined must not move
sel.setExcludeUndefined(true);
// don't ignore undefined or the caller might want to use its order
sel.setExcludeUndefined(false);
// unique index prend aussi en compte les archivés
sel.setArchivedPolicy(SQLSelect.BOTH);
sel.addSelect(t.getKey());
411,50 → 455,22
sel.addSelect(t.getOrderField());
if (t.isArchivable())
sel.addSelect(t.getArchiveField());
sel.setWhere(new Where(t.getOrderField(), diff < 0 ? "<" : ">", destOrder));
final Where orderWhere = Where.createRaw(t.getOrderField().getFieldRef() + (diff < 0 ? "<=" : ">=") + "(" + selOrder + ")", t.getOrderField());
// this.getWhere() needed when ORDER is null
sel.setWhere(orderWhere.or(this.getWhere()));
sel.addFieldOrder(t.getOrderField(), diff < 0 ? Order.desc() : Order.asc());
sel.setLimit(1);
sel.setLimit(2);
sel.setLockStrength(LockStrength.UPDATE);
 
final SQLDataSource ds = t.getBase().getDataSource();
@SuppressWarnings("unchecked")
final Map<String, Object> otherMap = ds.execute1(sel.asString());
if (otherMap != null) {
return new SQLRow(t, otherMap);
} else {
final List<SQLRow> rows = SQLRowListRSH.execute(sel);
assert rows.size() <= 2;
if (rows.isEmpty()) {
return null;
}
}
 
/**
* The free order just after or before this row.
*
* @param after whether to look before or after this row.
* @return a free order, or <code>null</code> if there's no room left.
*/
public final BigDecimal getOrder(boolean after) {
final BigDecimal destOrder = this.getOrder();
final SQLRow otherRow = this.getRow(after);
final BigDecimal otherOrder;
if (otherRow != null) {
otherOrder = otherRow.getOrder();
} else if (after) {
// dernière ligne de la table
otherOrder = destOrder.add(ReOrder.DISTANCE);
} else {
// première ligne
otherOrder = ReOrder.MIN_ORDER;
assert rows.get(0).equals(this);
return new List2<>(rows.get(0), rows.size() == 1 ? null : rows.get(1));
}
 
final int decDigits = this.getTable().getOrderDecimalDigits();
final BigDecimal least = BigDecimal.ONE.scaleByPowerOfTen(-decDigits);
final BigDecimal distance = destOrder.subtract(otherOrder).abs();
if (distance.compareTo(least) <= 0)
return null;
else {
final BigDecimal mean = destOrder.add(otherOrder).divide(BigDecimal.valueOf(2));
return DecimalUtils.round(mean, decDigits);
}
}
 
@Override
public SQLRow getForeign(String fieldName) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLField.java
391,7 → 391,7
* @see #getFieldGroup()
*/
public boolean isForeignKey() {
final FieldGroup fieldGroup = getFieldGroup();
final FieldGroup fieldGroup = getContainingFieldGroup();
return fieldGroup.getKeyType() == Type.FOREIGN_KEY && fieldGroup.getSingleField() != null;
}
 
401,10 → 401,23
* @return the group of this field.
* @see SQLTable#getFieldGroups()
*/
public FieldGroup getFieldGroup() {
public FieldGroup getContainingFieldGroup() {
return this.getTable().getFieldGroups().get(this.getName());
}
 
/**
* The group consisting of this single field.
*
* @return the group consisting of this single field.
* @throws IllegalStateException if this field is part of a multi-field group.
*/
public FieldGroup getFieldGroup() throws IllegalStateException {
final FieldGroup fg = this.getTable().getFieldGroups().get(this.getName());
if (fg.getSingleField() == null)
throw new IllegalStateException(this + " is part of a group with others : " + fg);
return fg;
}
 
public final SQLTable getForeignTable() {
return this.getDBSystemRoot().getGraph().getForeignTable(this);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/OrderComparator.java
15,6 → 15,7
 
import org.openconcerto.utils.CompareUtils;
 
import java.math.BigDecimal;
import java.util.Comparator;
 
/**
24,11 → 25,14
*/
public class OrderComparator implements Comparator<SQLRowAccessor> {
 
public static final Comparator<BigDecimal> BD_NULLS_FIRST = Comparator.nullsFirst(Comparator.naturalOrder());
public static final Comparator<BigDecimal> BD_NULLS_LAST = Comparator.nullsLast(Comparator.naturalOrder());
 
/**
* Order rows by {@link SQLTable#getOrderField()}.
*/
public static final OrderComparator INSTANCE = new OrderComparator(false);
private static final OrderComparator INSTANCE_FALLBACK_TO_PK = new OrderComparator(true);
public static final OrderComparator INSTANCE = new OrderComparator(false, BD_NULLS_LAST);
private static final OrderComparator INSTANCE_FALLBACK_TO_PK = new OrderComparator(true, BD_NULLS_LAST);
 
/**
* Order rows by {@link SQLTable#getOrderField()}, or if the table is not
41,11 → 45,12
}
 
private final boolean fallbackToPK;
private final Comparator<BigDecimal> bdComparator;
 
// singleton
private OrderComparator(boolean fallbackToPK) {
public OrderComparator(final boolean fallbackToPK, final Comparator<BigDecimal> bdComparator) {
super();
this.fallbackToPK = fallbackToPK;
this.bdComparator = bdComparator;
}
 
@Override
57,8 → 62,11
final SQLTable t = r1.getTable();
if (!t.equals(r2.getTable()))
throw new IllegalArgumentException(r1 + " and " + r2 + " are not of the same table");
if (t.isOrdered()) {
return r1.getOrder().compareTo(r2.getOrder());
final SQLField orderField = t.getTable().getOrderField();
if (orderField != null) {
final BigDecimal order1 = r1.getObjectAs(orderField.getName(), true, BigDecimal.class);
final BigDecimal order2 = r2.getObjectAs(orderField.getName(), true, BigDecimal.class);
return this.bdComparator.compare(order1, order2);
} else if (this.fallbackToPK) {
if (!t.isRowable())
throw new IllegalArgumentException(t + " neither ordered nor rowable");
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValues.java
1218,25 → 1218,21
* @return this.
*/
public SQLRowValues setOrder(SQLRow r, boolean after) {
return this.setOrder(r, after, ReOrder.DISTANCE.movePointRight(2).intValue(), 0);
setOrder(Collections.singletonList(this), r, after);
return this;
}
 
private SQLRowValues setOrder(SQLRow r, boolean after, int nbToReOrder, int nbReOrdered) {
final BigDecimal freeOrder = r.getOrder(after);
final String orderName = this.getTable().getOrderField().getName();
if (freeOrder != null)
return this.put(orderName, freeOrder);
else if (nbReOrdered > r.getTable().getRowCount()) {
throw new IllegalStateException("cannot reorder " + r.getTable().getSQLName());
} else {
// make room
public static void setOrder(final List<SQLRowValues> values, final SQLRow r, boolean after) {
final int valuesCount = values.size();
final List<BigDecimal> orders;
try {
ReOrder.create(this.getTable(), r.getOrder().intValue() - (nbToReOrder / 2), nbToReOrder).exec();
orders = ReOrder.getFreeOrderValuesFor(valuesCount, after, r).get0();
} catch (SQLException e) {
throw ExceptionUtils.createExn(IllegalStateException.class, "reorder failed for " + this.getTable() + " at " + r.getOrder(), e);
throw ExceptionUtils.createExn(IllegalStateException.class, "reorder failed for " + r.getTable() + " at " + r.getOrder(), e);
}
r.fetchValues();
return this.setOrder(r, after, nbToReOrder * 10, nbToReOrder);
final String orderName = r.getTable().getOrderField().getName();
for (int i = 0; i < valuesCount; i++) {
values.get(i).put(orderName, orders.get(i));
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowAccessor.java
402,9 → 402,13
}
 
public final <T> T getObjectAs(String field, Class<T> clazz) {
return this.getObjectAs(field, false, clazz);
}
 
public final <T> T getObjectAs(final String field, final boolean mustBePresent, final Class<T> clazz) {
T res = null;
try {
res = clazz.cast(this.getObject(field));
res = clazz.cast(this.getObject(field, mustBePresent));
} catch (ClassCastException e) {
throw new IllegalArgumentException("Impossible d'accéder au champ " + field + " de la ligne " + this + " en tant que " + clazz.getSimpleName(), e);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/CompanyAccessSQLElement.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightSQLElement.java
14,14 → 14,13
package org.openconcerto.sql.users.rights;
 
import static java.util.Arrays.asList;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.ConfSQLElement;
 
import org.openconcerto.sql.element.GlobalMapper;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.utils.SQLCreateTable;
import org.openconcerto.utils.i18n.I18nUtils;
 
import java.util.ArrayList;
import java.util.Collections;
28,7 → 27,7
import java.util.List;
import java.util.Set;
 
public class UserRightSQLElement extends ConfSQLElement {
public class UserRightSQLElement extends SQLElement {
public static final String TABLE_NAME = "USER_RIGHT";
 
static public List<SQLCreateTable> getCreateTables(final SQLTable userT) {
56,9 → 55,8
return res;
}
 
public UserRightSQLElement() {
super(TABLE_NAME);
this.setL18nPackageName(I18nUtils.getPackageName(TM.class));
public UserRightSQLElement(final DBRoot r) {
super(r.findTable(TABLE_NAME), null, "sql.user-right");
final UserRightGroup group = new UserRightGroup();
GlobalMapper.getInstance().map(UserRightSQLComponent.ID, group);
setDefaultGroup(group);
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/RightSQLElement.java
13,20 → 13,18
package org.openconcerto.sql.users.rights;
 
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.ConfSQLElement;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.UISQLComponent;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.utils.SQLCreateTable;
import org.openconcerto.ui.component.ITextArea;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.i18n.I18nUtils;
 
import java.util.Arrays;
import java.util.List;
 
public class RightSQLElement extends ConfSQLElement {
public class RightSQLElement extends SQLElement {
 
public static final String TABLE_NAME = "RIGHT";
 
38,9 → 36,8
return res;
}
 
public RightSQLElement() {
super(TABLE_NAME);
this.setL18nPackageName(I18nUtils.getPackageName(TM.class));
public RightSQLElement(final DBRoot root) {
super(root.findTable(TABLE_NAME), null, "sql.right");
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightsManager.java
289,7 → 289,7
};
this.javaRights = new JavaRights();
this.table = t;
this.toUserLink = t.getFieldGroups().get("ID_USER_COMMON").getKey().getForeignLink();
this.toUserLink = t.getField("ID_USER_COMMON").getFieldGroup().getForeignLink();
defaultRegister();
this.userRights = new HashMap<Integer, UserRights>();
this.exec = Executors.newSingleThreadExecutor(new ThreadFactory(this.getClass().getSimpleName() + " executor for " + t.getSQLName(), true).setPriority(Thread.MIN_PRIORITY));
/trunk/OpenConcerto/src/org/openconcerto/sql/users/UserCommonSQLElement.java
16,7 → 16,6
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.BaseSQLComponent;
import org.openconcerto.sql.element.ConfSQLElement;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.DBRoot;
47,7 → 46,6
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.checks.ValidState;
import org.openconcerto.utils.i18n.I18nUtils;
import org.openconcerto.utils.text.SimpleDocumentListener;
 
import java.awt.Component;
75,7 → 73,7
import javax.swing.table.DefaultTableCellRenderer;
 
// FIXME Login user unique ?
public class UserCommonSQLElement extends ConfSQLElement {
public class UserCommonSQLElement extends SQLElement {
 
/**
* Set this system property to "true" if this should generate old style passwords.
82,19 → 80,15
*/
public static final String LEGACY_PASSWORDS = "org.openconcerto.sql.legacyPasswords";
 
{
this.setL18nPackageName(I18nUtils.getPackageName(TM.class));
}
 
private final boolean familyNameFirst;
 
public UserCommonSQLElement() {
super("USER_COMMON");
this.familyNameFirst = false;
public UserCommonSQLElement(final DBRoot root) {
this(root, false);
}
 
public UserCommonSQLElement(final DBRoot root, final boolean familyNameFirst) {
super(root.findTable("USER_COMMON"));
// allow subclass to keep same code
super(root.findTable("USER_COMMON"), null, "sql.user");
this.familyNameFirst = familyNameFirst;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/TextSearchSpec.java
85,6 → 85,18
return res;
}
 
private final String format(final Format fmt, final Object cell) {
try {
return fmt.format(cell);
} catch (Exception e) {
throw new IllegalStateException("Couldn't format " + cell + '(' + getClass(cell) + ") with " + fmt, e);
}
}
 
static private String getClass(Object cell) {
return cell == null ? "<null>" : cell.getClass().getName();
}
 
private final Double getDouble() {
if (!this.parsedFilterD_tried) {
try {
131,7 → 143,7
// which is better achieved with contains)
// PS: the date format is useful for parsing since "> 25/12/2010" means
// "> 25/12/2010 00:00" which is understandable and concise.
if (isContains && containsOrEquals(fmt.format(cell)))
if (isContains && containsOrEquals(format(fmt, cell)))
return true;
// e.g. test if "01/01/2006" is before "25 déc. 2010"
else if (!isContains && test(getParsed(fmt), cell))
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListe.java
370,7 → 370,7
 
public String getLine(final boolean created, final SQLRowValues row, final SQLField userF, final SQLField dateF) {
final Calendar date = dateF == null ? null : row.getDate(dateF.getName());
final SQLRowAccessor user = userF == null || row.isForeignEmpty(userF.getName()) ? null : row.getForeign(userF.getName());
final SQLRowAccessor user = userF == null || row.getObject(userF.getName()) == null || row.isForeignEmpty(userF.getName()) ? null : row.getForeign(userF.getName());
if (user == null && date == null)
return null;
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ListSQLLine.java
275,8 → 275,8
}
if (toRemove != null)
toRemove.remove(lastField.getName());
// attach updated values
if (vals != null && vals.getLong(lastField.getName()) == target.getIDNumber().longValue())
// attach updated values, foreign ID is always present but can be null
if (vals != null && target.getIDNumber().equals(vals.getForeignIDNumberValue(lastField.getName()).getValue()))
vals.deepCopy().put(lastField.getName(), target);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/MoveQueue.java
18,17 → 18,19
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.utils.ReOrder;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.SleepingQueue;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
131,20 → 133,13
// if only some rows are moved, update one by one (avoids refreshing the whole list)
// (getRowCount() is not thread-safe, so use getTotalRowCount())
if (rowCount < 5 && rowCount < (this.tableModel.getTotalRowCount() / 3)) {
final List<?> l;
if (after) {
// If we want to put X,Y after A in the list A,B,C
// we need to pass first Y : A,Y,B,C then X :
// A,X,Y,B,C
l = new ArrayList<Object>(srcRows);
Collections.reverse(l);
} else {
l = srcRows;
}
final SQLTable t = getTable();
final String orderName = t.getOrderField().getName();
final SQLRowValues vals = new SQLRowValues(t);
for (final Object src : l) {
vals.setOrder(destRow, after).update(getID(src, t).intValue());
final List<BigDecimal> orders = ReOrder.getFreeOrderValuesFor(rowCount, after, destRow).get0();
for (int i = 0; i < rowCount; i++) {
final Object src = srcRows.get(i);
vals.put(orderName, orders.get(i)).update(getID(src, t).intValue());
}
} else {
// update all rows at once and refresh the whole list
184,75 → 179,64
moveAtOnce(srcRows, srcRows.size(), after, destRow);
}
 
/**
* Move the passed rows just before or just after the destination row if needed.
*
* @param ids rows to order.
* @param after <code>true</code> if the rows should be placed after <code>destRow</code>,
* <code>false</code> otherwise.
* @param destRow <code>srcRows</code> will be placed relative to the order of this row.
* @param checkBefore if <code>true</code> the current DB order will be checked and if it
* matched the passed rows, no change will be made to the DB, <code>false</code> to
* always change the orders in the DB.
* @return if the DB was changed.
* @throws SQLException if an error occurs.
*/
static public boolean moveIDsAtOnce(final List<? extends Number> ids, final boolean after, final SQLRow destRow, final boolean checkBefore) throws SQLException {
final int size = ids.size();
if (checkBefore) {
final SQLTable table = destRow.getTable();
final SQLSelect sel = new SQLSelect(true);
sel.addSelect(table.getKey());
sel.addOrder(table);
sel.setWhere(new Where(table.getKey(), ids));
// needed since we might update them just after in moveAtOnce(), and it's safer to lock
// with the most restrictive mode first.
sel.setLockStrength(LockStrength.UPDATE);
final List<?> dbOrderedIDs = table.getDBSystemRoot().getDataSource().executeCol(sel.asString());
if (dbOrderedIDs.size() != size)
throw new IllegalStateException("Missing rows");
boolean orderOK = true;
for (int i = 0; i < size && orderOK; i++) {
final Number passedID = ids.get(i);
final Number dbID = (Number) dbOrderedIDs.get(i);
orderOK = NumberUtils.areNumericallyEqual(passedID, dbID);
}
if (orderOK)
return false;
}
moveAtOnce(ids, size, after, destRow);
return true;
}
 
static private void moveAtOnce(final List<?> srcRows, final int rowCount, final boolean after, final SQLRow destRow) throws SQLException {
if (rowCount == 0)
return;
final SQLTable t = destRow.getTable();
 
// ULP * 10 to give a little breathing room
final BigDecimal minDistance = t.getOrderULP().scaleByPowerOfTen(1);
assert minDistance.signum() > 0;
final BigDecimal places = BigDecimal.valueOf(rowCount + 1);
// the minimum room so that we can move all rows
final BigDecimal room = minDistance.multiply(places);
final List<BigDecimal> freeOrderValues = ReOrder.getFreeOrderValuesFor(rowCount, after, destRow).get0();
 
final BigDecimal destOrder = destRow.getOrder();
final SQLRow nextRow = destRow.getRow(true);
final BigDecimal inc;
final boolean destRowReordered;
if (nextRow == null) {
// if destRow is the last row, we can choose whatever increment we want
inc = ReOrder.DISTANCE;
// but we need to move destRow if we want to add before it
destRowReordered = false;
} else {
final BigDecimal nextOrder = nextRow.getOrder();
assert nextOrder.compareTo(destOrder) > 0;
final BigDecimal diff = nextOrder.subtract(destOrder);
assert diff.signum() > 0;
if (diff.compareTo(room) < 0) {
// if there's not enough room, reorder to squeeze rows upwards
// since we keep increasing count, we will eventually reorder all rows afterwards
int count = 100;
final int tableRowCount = t.getRowCount();
boolean reordered = false;
while (!reordered) {
// only push destRow upwards if we want to add before
reordered = ReOrder.create(t, destOrder, !after, count, destOrder.add(room)).exec();
if (!reordered && count > tableRowCount)
throw new IllegalStateException("Unable to reorder " + count + " rows in " + t);
count *= 10;
}
inc = minDistance;
destRowReordered = true;
} else {
// truncate
inc = DecimalUtils.round(diff.divide(places, DecimalUtils.HIGH_PRECISION), t.getOrderDecimalDigits(), RoundingMode.DOWN);
destRowReordered = false;
}
}
// i.e. inc > 0
assert inc.compareTo(minDistance) >= 0;
 
BigDecimal newOrder = destOrder;
// by definition if we want to add after, destOrder should remain unchanged
if (after) {
newOrder = newOrder.add(inc);
}
final List<List<String>> newOrdersAndIDs = new ArrayList<List<String>>(rowCount);
int i = 0;
final List<Number> ids = rowCount < 10 ? new ArrayList<Number>(rowCount) : null;
// we go from newOrder and up, so that the passed rows are in ascending order
for (final Object src : srcRows) {
final Number srcID = getID(src, t);
if (ids != null)
ids.add(srcID);
final BigDecimal newOrder = freeOrderValues.get(i++);
newOrdersAndIDs.add(Arrays.asList(srcID.toString(), newOrder.toPlainString()));
newOrder = newOrder.add(inc);
}
// move out before general request as most DB systems haven't got DEFERRABLE constraints
if (!after && !destRowReordered) {
final UpdateBuilder updateDestRow = new UpdateBuilder(t);
updateDestRow.setObject(t.getOrderField(), newOrder);
updateDestRow.setWhere(destRow.getWhere());
t.getDBSystemRoot().getDataSource().execute(updateDestRow.asString());
}
 
final SQLSyntax syntax = SQLSyntax.get(t);
final UpdateBuilder update = new UpdateBuilder(t);
261,6 → 245,12
update.setFromVirtualJoinField(t.getOrderField().getName(), constantTableAlias, "newOrder");
t.getDBSystemRoot().getDataSource().execute(update.asString());
 
t.fireTableModified(SQLRow.NONEXISTANT_ID, Collections.singletonList(t.getOrderField().getName()));
final List<String> fieldsChanged = Collections.singletonList(t.getOrderField().getName());
if (ids == null) {
t.fireTableModified(SQLRow.NONEXISTANT_ID, fieldsChanged);
} else {
for (final Number id : ids)
t.fireTableModified(id.intValue(), fieldsChanged);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/UpdateQueue.java
140,7 → 140,7
this.put(new SetStateRunnable() {
@Override
public void run() {
getModel().getSearchQueue().start();
getModel().startSearchQueue();
}
});
}
244,7 → 244,8
}
// if the modified row isn't in the existing line, it might still affect it
// if it's a referent row insertion
if (!put && lastReferentField != null && r.exists()) {
if (!put && lastReferentField != null && r.exists() && !r.isForeignEmpty(lastReferentField)) {
// no NPE, even without an undefined ID since we tested isForeignEmpty()
final int foreignID = r.getInt(lastReferentField);
for (final SQLRowValues current : line.getRow().followPath(p.minusLast(), CreateMode.CREATE_NONE, false)) {
if (current.getID() == foreignID) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/TextTableCellEditorWithCompletion.java
17,6 → 17,8
import org.openconcerto.ui.TextAreaTableCellEditor;
import org.openconcerto.utils.checks.ValidState;
 
import java.awt.Component;
 
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
56,7 → 58,17
 
this.textWithCompl.hidePopup();
if (!getValidState().isValid()) {
JOptionPane.showMessageDialog(SwingUtilities.getRoot(this.getTextArea()), getValidState().getValidationText());
final Component root = SwingUtilities.getRoot(TextTableCellEditorWithCompletion.this.getTextArea());
final String validationText = getValidState().getValidationText();
// JOptionPane dans invokeLater pour éviter une boucle avec notre listener sur le focus
// de la table qui stoppe
// l'édition
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(root, validationText);
}
});
return false;
} else {
return super.stopCellEditing();
63,7 → 75,7
}
}
 
@Override
public void setLimitedSize(int nbChar) {
this.textWithCompl.setLimitedSize(nbChar);
}
73,5 → 85,3
}
 
}
 
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTextComboTableCellEditor.java
25,6 → 25,7
import org.openconcerto.ui.FrameUtil;
import org.openconcerto.ui.list.selection.BaseListStateModel;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.convertor.NumberConvertor;
 
import java.awt.Color;
import java.awt.Component;
48,7 → 49,7
 
private Where w;
// Stock Value of Combo to fix problem with undefined
int val = 1;
Integer val = 1;
 
boolean addUndefined;
 
123,18 → 124,19
}
 
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if (value != null) {
 
this.val = (Integer) value;
this.comboBox.setValue(this.val);
}
 
this.comboBox.grabFocus();
 
this.comboBox.addModelListener("wantedID", new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
SQLTextComboTableCellEditor.this.val = comboBox.getWantedID();
SQLTextComboTableCellEditor.this.val = NumberConvertor.convertExact((Number) evt.getNewValue(), Integer.class);
}
});
 
// Filtre sur une valeur specifique
if (this.fieldWhere != null && table instanceof RowValuesTable) {
RowValuesTable rowVals = (RowValuesTable) table;
160,7 → 162,7
}
 
public int getComboSelectedId() {
return SQLTextComboTableCellEditor.this.comboBox.getSelectedId();
return SQLTextComboTableCellEditor.this.comboBox.getWantedID();
}
 
private SQLField fieldWhere;
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSourceOnline.java
49,7 → 49,7
assert SwingUtilities.isEventDispatchThread();
if (this.moveQ == null) {
this.moveQ = new MoveQueue(getModel());
this.moveQ.start();
getModel().startQueue(this.moveQ);
}
return this.moveQ;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowValuesTableControlPanel.java
99,8 → 99,12
this.buttonInserer = new JButton(TM.tr("insertNewLine"));
this.buttonInserer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
RowValuesTableControlPanel.this.model.addNewRowAt(table.getSelectedRow());
int index = table.getSelectedRow();
if (index < 0 || index > table.getRowCount()) {
index = table.getRowCount();
}
RowValuesTableControlPanel.this.model.addNewRowAt(index);
}
});
this.buttonInserer.setEnabled(false);
c.gridx++;
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ITableModel.java
38,6 → 38,7
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
67,6 → 68,7
* addTableModelListener()) it dies and cannot be used again.
*
* @author Sylvain CUAZ
* @see #start()
*/
public class ITableModel extends AbstractTableModel {
public static enum SleepState {
169,6 → 171,8
private boolean cellsEditable, orderEditable;
private boolean debug;
 
@GuardedBy("this")
private UncaughtExceptionHandler uncaughtExnHandler = null;
private DyingQueueExceptionHandler dyingQueueHandler = null;
 
public ITableModel(SQLTableModelSource src) {
880,7 → 884,14
super.addTableModelListener(l);
}
 
// TODO no longer leak this in our constructor
public synchronized final void setUncaughtExceptionHandler(UncaughtExceptionHandler uncaughtExnHandler) {
this.uncaughtExnHandler = uncaughtExnHandler;
}
 
public synchronized final UncaughtExceptionHandler getUncaughtExceptionHandler() {
return this.uncaughtExnHandler;
}
 
public final void start() {
final RunningState state = this.updateQ.getRunningState();
if (state.compareTo(RunningState.RUNNING) > 0)
888,10 → 899,20
if (state == RunningState.NEW) {
print("starting");
this.getLinesSource().live();
this.updateQ.start();
this.startQueue(this.updateQ);
}
}
 
final void startSearchQueue() {
this.startQueue(this.getSearchQueue());
}
 
final void startQueue(final SleepingQueue q) {
q.start((thr) -> {
thr.setUncaughtExceptionHandler(getUncaughtExceptionHandler());
});
}
 
@Override
public void removeTableModelListener(TableModelListener l) {
assert SwingUtilities.isEventDispatchThread();
928,6 → 949,7
* {@link SleepingQueue#die()}, <code>null</code> to reset default behavior.
*/
public void setDyingQueueExceptionHandler(final DyingQueueExceptionHandler h) {
assert SwingUtilities.isEventDispatchThread();
this.dyingQueueHandler = h;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/SQLMenuItemHelper.java
18,6 → 18,7
import org.openconcerto.utils.cc.IClosure;
 
import java.awt.event.ActionEvent;
import java.util.Objects;
 
import javax.swing.AbstractAction;
import javax.swing.Action;
149,15 → 150,15
return menuItemAction;
}
 
public static abstract class AbstractSQLMenuItemAction extends AbstractAction {
public static abstract class AbstractSQLMenuItemAction<E extends SQLElement> extends AbstractAction {
 
private final SQLElement elem;
private final E elem;
private JFrame frame;
private boolean cacheFrame;
 
public AbstractSQLMenuItemAction(SQLElement elem, String name) {
public AbstractSQLMenuItemAction(E elem, String name) {
super(name);
this.elem = elem;
this.elem = Objects.requireNonNull(elem, "SQLElement");
this.frame = null;
this.cacheFrame = true;
this.putValue(Action.ACTION_COMMAND_KEY, getClass().getName() + " with " + getElem().getCode());
188,7 → 189,7
 
protected abstract JFrame createFrame();
 
public final SQLElement getElem() {
public final E getElem() {
return this.elem;
}
 
198,7 → 199,7
}
}
 
static abstract class GenericSQLElementAction<F extends JFrame> extends AbstractSQLMenuItemAction {
static abstract class GenericSQLElementAction<F extends JFrame> extends AbstractSQLMenuItemAction<SQLElement> {
 
public GenericSQLElementAction(SQLElement elem, String name) {
super(elem, name);
/trunk/OpenConcerto/src/org/openconcerto/sql/PropsConfiguration.java
14,6 → 14,7
package org.openconcerto.sql;
 
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.element.SQLElementNamesFromXML;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBStructureItem;
import org.openconcerto.sql.model.DBSystemRoot;
36,6 → 37,7
import org.openconcerto.utils.NetUtils;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.ReflectUtils;
import org.openconcerto.utils.StreamUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Value;
372,6 → 374,13
return rootIsBase ? this.getRootName() : this.getSystemRootName();
}
 
private final String toClassName(final String rsrcName) {
if (rsrcName.charAt(0) == '/')
return rsrcName.substring(1).replace('/', '.');
else
return this.getResourceWD().getPackage().getName() + '.' + rsrcName.replace('/', '.');
}
 
/**
* Return the correct stream depending on file mode. If file mode is
* {@link FileMode#NORMAL_FILE} it will first check if a file named <code>name</code> exists,
383,7 → 392,7
public final InputStream getStream(final String name) {
final File f = getFile(name);
if (mustUseClassloader(f)) {
return this.getClass().getResourceAsStream(name);
return getResourceWD().getResourceAsStream(name);
} else
try {
return new FileInputStream(f);
392,6 → 401,11
}
}
 
// the "working directory" where relative names are resolved
protected Class<? extends PropsConfiguration> getResourceWD() {
return this.getClass();
}
 
private File getFile(final String name) {
return new File(name.startsWith("/") ? name.substring(1) : name);
}
403,7 → 417,7
public final String getResource(final String name) {
final File f = getFile(name);
if (mustUseClassloader(f)) {
return this.getClass().getResource(name).toExternalForm();
return this.getResourceWD().getResource(name).toExternalForm();
} else {
return f.getAbsolutePath();
}
967,7 → 981,8
return new SQLElementDirectory();
}
 
// items will be passed to #getStream(String)
// Use resource name to be able to use absolute (beginning with /) or relative path (to this
// class)
protected List<String> getMappings() {
return Arrays.asList("mapping", "mapping-" + this.getProperty("customer"));
}
977,7 → 992,7
if (mappings.size() == 0)
throw new IllegalStateException("empty mappings");
 
final SQLFieldTranslator trns = new SQLFieldTranslator(this.getRoot(), null, dir);
final SQLFieldTranslator trns = new SQLFieldTranslator(this.getRoot(), dir);
// perhaps listen to UserProps (as in TM)
return loadTranslations(trns, this.getRoot(), mappings);
}
995,20 → 1010,47
final ListIterator<Locale> listIterator = CollectionUtils.getListIterator(langs, true);
while (listIterator.hasNext()) {
final Locale lang = listIterator.next();
found |= loadTranslations(trns, PropsConfiguration.class.getResourceAsStream(cntrl.toBundleName("mapping", lang) + ".xml"), root);
final SQLElementNamesFromXML elemNames = new SQLElementNamesFromXML(lang);
found |= loadTranslations(trns, PropsConfiguration.class.getResourceAsStream(cntrl.toBundleName("mapping", lang) + ".xml"), root, elemNames);
for (final String m : mappings) {
found |= loadTranslations(trns, this.getStream(cntrl.toBundleName(m, lang) + ".xml"), root);
final String bundleName = cntrl.toBundleName(m, lang);
found |= loadTranslations(trns, this.getStream(bundleName + ".xml"), root, elemNames);
final Class<? extends TranslatorFiller> loadedClass = ReflectUtils.getSubclass(toClassName(bundleName), TranslatorFiller.class);
if (loadedClass != null) {
try {
ReflectUtils.createInstance(loadedClass, this).fill(trns);
} catch (Exception e) {
Log.get().log(Level.WARNING, "Couldn't use " + loadedClass, e);
}
}
}
}
}
return trns;
}
 
private final boolean loadTranslations(final SQLFieldTranslator trns, final InputStream in, final DBRoot root) {
@FunctionalInterface
static public interface TranslatorFiller {
void fill(final SQLFieldTranslator t);
}
 
static public abstract class AbstractTranslatorFiller implements TranslatorFiller {
private final PropsConfiguration conf;
 
public AbstractTranslatorFiller(final PropsConfiguration conf) {
this.conf = conf;
}
 
protected final PropsConfiguration getConf() {
return this.conf;
}
}
 
private final boolean loadTranslations(final SQLFieldTranslator trns, final InputStream in, final DBRoot root, final SQLElementNamesFromXML elemNames) {
final boolean res = in != null;
// do not force to have one mapping for each client and each locale
if (res)
trns.load(root, in);
trns.load(root, in, elemNames);
return res;
}
 
1294,11 → 1336,6
}
 
@Override
public final SQLFieldTranslator getTranslator() {
return this.getDirectory().getTranslator();
}
 
@Override
public final SQLElementDirectory getDirectory() {
return this.directory.get();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/mapping_en.xml
1,6 → 1,7
<?xml version="1.0" encoding="UTF-8" ?>
<ROOT>
<TABLE name="USER_COMMON">
<translations>
<element refid="sql.user">
<name base="user" />
<FIELD name="NOM" label="Last Name" />
<FIELD name="PRENOM" label="First name" />
<FIELD name="PASSWORD" label="Password" />
12,17 → 13,21
<FIELD name="MAIL" label="E-Mail" />
<FIELD name="DISABLED" label="Account disabled" />
<FIELD name="TEL" label="Phone" titlelabel="Phone" />
</TABLE>
<TABLE name="RIGHT">
</element>
<element refid="sql.right">
<name base="right" />
<FIELD name="CODE" label="Code" />
<FIELD name="NOM" label="Name" titlelabel="Name of right" />
<FIELD name="DESCRIPTION" label="Description" titlelabel="Desc." />
</TABLE>
<TABLE name="USER_RIGHT">
</element>
<element refid="sql.user-right">
<name base="user right">
<variant refids="plural" value="users rights" />
</name>
<FIELD name="ID_USER_COMMON" label="User" />
<FIELD name="ID_RIGHT" label="Right" />
<FIELD name="OBJECT" label="Object" />
<FIELD name="user.right.parameters.editor" label="Object" />
<FIELD name="HAVE_RIGHT" label="Right granted" titlelabel="Granted" />
</TABLE>
</ROOT>
</element>
</translations>
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightAutoCompleteComboBox.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightRowValuesTableOnline.java
40,8 → 40,12
import net.minidev.json.JSONObject;
 
public class LightRowValuesTableOnline extends LightRowValuesTable {
private final ITransformer<SQLSelect, SQLSelect> orginTransformer;
private ITransformer<SQLSelect, SQLSelect> orginTransformer;
 
public LightRowValuesTableOnline() {
// Serialization
}
 
public LightRowValuesTableOnline(final Configuration configuration, final Number userId, final String id, final ITableModel model) {
super(configuration, userId, id, model);
 
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightUISQLComboRequest.java
New file
0,0 → 1,50
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
import org.openconcerto.ui.light.LightUIComboBoxElement;
import org.openconcerto.ui.light.LightUIComboRequest;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Pattern;
 
public class LightUISQLComboRequest implements LightUIComboRequest {
 
private final static Pattern QUERY_SPLIT_PATTERN = Pattern.compile("\\s+");
 
private final ComboSQLRequest request;
 
public LightUISQLComboRequest(ComboSQLRequest request) {
super();
this.request = request;
}
 
@Override
public List<LightUIComboBoxElement> getItems(String filter, Optional<LightUIComboBoxElement> selection) {
final Where where = selection.isPresent() ? new Where(this.request.getPrimaryTable().getKey(), "=", selection.get().getId()) : null;
final List<IComboSelectionItem> items = this.request.getComboItems(true, Arrays.asList(QUERY_SPLIT_PATTERN.split(filter)), Locale.getDefault(), where);
final List<LightUIComboBoxElement> res = new ArrayList<>(items.size());
for (final IComboSelectionItem item : items) {
res.add(new LightUIComboBoxElement(item.getId(), item.getLabel()));
}
return res;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/GroupToLightUIConvertor.java
13,7 → 13,6
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.FieldMapper;
25,6 → 24,7
import org.openconcerto.ui.group.Item;
import org.openconcerto.ui.group.LayoutHints;
import org.openconcerto.ui.light.CustomEditorProvider;
import org.openconcerto.ui.light.LightUIAutoCompleteComboBox;
import org.openconcerto.ui.light.LightUICheckBox;
import org.openconcerto.ui.light.LightUIDate;
import org.openconcerto.ui.light.LightUIElement;
32,8 → 32,9
import org.openconcerto.ui.light.LightUILabel;
import org.openconcerto.ui.light.LightUILine;
import org.openconcerto.ui.light.LightUIPanel;
import org.openconcerto.ui.light.LightUITextArea;
import org.openconcerto.ui.light.LightUITabbed;
import org.openconcerto.ui.light.LightUITextField;
import org.openconcerto.utils.Log;
import org.openconcerto.utils.i18n.TranslationManager;
 
import java.awt.Color;
78,8 → 79,10
 
final LightEditFrame editFrame = new LightEditFrame(this.configuration, group, defaultRow.asRowValues(), parentFrame, editMode);
final LightUIPanel framePanel = editFrame.getContentPanel();
append(sqlElement, framePanel, group);
 
Map<String, LightUITabbed> tabbedMap = new HashMap<>();
append(sqlElement, framePanel, group, tabbedMap);
 
String frameTitle = TranslationManager.getInstance().getTranslationForItem(group.getId());
if (frameTitle == null) {
frameTitle = group.getId();
91,11 → 94,12
return editFrame;
}
 
private void append(final SQLElement sqlElement, final LightUIPanel panel, final Item item) {
private void append(final SQLElement sqlElement, final LightUIPanel panel, final Item item, Map<String, LightUITabbed> tabbedMap) {
if (item instanceof Group) {
final Group gr = (Group) item;
int size = gr.getSize();
 
if (gr.getTabId() == null) {
final String groupTitle = TranslationManager.getInstance().getTranslationForItem(gr.getId());
final LightUIPanel childPanel = new LightUIPanel(gr.getId());
childPanel.setFillWidth(true);
120,7 → 124,7
}
 
for (int i = 0; i < size; i++) {
this.append(sqlElement, childPanel, gr.getItem(i));
this.append(sqlElement, childPanel, gr.getItem(i), tabbedMap);
}
 
final LightUILine line = new LightUILine();
127,6 → 131,36
line.addChild(childPanel);
panel.addChild(line);
} else {
String tabId = gr.getTabId();
LightUITabbed tabbed = tabbedMap.get(tabId);
if (tabbed == null) {
tabbed = new LightUITabbed(tabId) {
 
@Override
public void loadTab(String tabId) {
// TODO Auto-generated method stub
 
}
};
final LightUILine line = new LightUILine();
line.addChild(tabbed);
panel.addChild(line);
tabbedMap.put(tabId, tabbed);
}
// add the group in the tabbed
final LightUIPanel childPanel = new LightUIPanel(gr.getId());
childPanel.setFillWidth(true);
childPanel.setGridWidth(4);
String title = TranslationManager.getInstance().getTranslationForItem(gr.getId());
childPanel.setTitle(title);
for (int i = 0; i < size; i++) {
this.append(sqlElement, childPanel, gr.getItem(i), tabbedMap);
}
 
tabbed.addChild(childPanel);
 
}
} else {
final LayoutHints localHint = item.getLocalHint();
LightUILine currentLine = panel.getLastLine();
 
156,7 → 190,10
panel.addChild(currentLine);
}
 
final SQLField field = this.mapper.getSQLFieldForItem(item.getId());
SQLField field = this.mapper.getSQLFieldForItem(item.getId());
if (field == null) {
field = sqlElement.getTable().getFieldRaw(item.getId());
}
LightUILabel elementLabel = null;
 
String label = this.getLabelForItem(field, item);
191,19 → 228,13
if (field != null) {
Class<?> javaType = field.getType().getJavaType();
if (field.isKey()) {
elementEditor = new LightAutoCompleteComboBox(item.getId());
elementEditor = new LightUIAutoCompleteComboBox(item.getId());
elementEditor.setMinInputSize(20);
elementEditor.setValueType(LightUIElement.VALUE_TYPE_REF);
} else if (javaType.equals(String.class)) {
if (field.getType().getSize() > 1000) {
elementEditor = new LightUITextArea(item.getId());
elementEditor.setValue("");
elementEditor.setMinInputSize(10);
} else {
elementEditor = new LightUITextField(item.getId());
elementEditor.setValue("");
elementEditor.setMinInputSize(10);
}
elementEditor.setValueType(LightUIElement.VALUE_TYPE_STRING);
} else if (javaType.equals(Boolean.class)) {
elementEditor = new LightUICheckBox(item.getId(), "");
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightRowValuesTable.java
51,12 → 51,16
 
private int offset = 0;
 
private ITableModel model;
private transient ITableModel model;
 
private final ITransformer<SQLSelect, SQLSelect> orginTransformer;
private transient ITransformer<SQLSelect, SQLSelect> orginTransformer;
 
private List<TableModelListener> tableModelListeners = new ArrayList<TableModelListener>();
private transient List<TableModelListener> tableModelListeners = new ArrayList<TableModelListener>();
 
public LightRowValuesTable() {
// Serialization
}
 
public LightRowValuesTable(final Configuration configuration, final Number userId, final String id, final ITableModel model) {
super(id);
 
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightEditFrame.java
27,7 → 27,6
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.group.Item;
import org.openconcerto.ui.light.CustomEditorProvider;
import org.openconcerto.ui.light.JSONToLightUIConvertor;
import org.openconcerto.ui.light.LightUICheckBox;
import org.openconcerto.ui.light.LightUIComboBox;
import org.openconcerto.ui.light.LightUIDate;
35,6 → 34,9
import org.openconcerto.ui.light.LightUIFrame;
import org.openconcerto.utils.io.JSONConverter;
 
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Timestamp;
52,6 → 54,10
 
private EditMode editMode = EditMode.READONLY;
 
public LightEditFrame() {
// Serialization
}
 
// Init from json constructor
public LightEditFrame(final JSONObject json) {
super(json);
176,18 → 182,20
}
}
 
final protected void putValueFromUserControl(final Configuration configuration, final SQLElement sqlElement, final SQLField sqlField, final LightUIElement uiElement,
protected final void putValueFromUserControl(final Configuration configuration, final SQLElement sqlElement, final SQLField sqlField, final LightUIElement uiElement,
final Map<String, CustomEditorProvider> customEditors) {
if (!uiElement.isNotSaved()) {
boolean useElementValue = true;
final Class<?> fieldType = sqlField.getType().getJavaType();
if (customEditors.containsKey(uiElement.getId())) {
final CustomEditorProvider customEditor = customEditors.get(uiElement.getId());
if (customEditor instanceof SavableCustomEditorProvider) {
((SavableCustomEditorProvider) customEditor).save(this.sqlRow, sqlField, uiElement);
} else {
throw new IllegalStateException(customEditor + " must implement SavableCustomEditorProvider for field " + sqlField.getFieldName() + " of tye " + fieldType);
useElementValue = false;
}
} else {
}
 
if (useElementValue) {
final String fieldName = sqlField.getFieldName();
if (sqlField.isKey()) {
if (!(uiElement instanceof LightUIComboBox)) {
297,13 → 305,17
final SQLTable sqlTable = this.sqlRow.getTable();
final Date now = new Date();
// FIXME only set those fields at insertion time
if (sqlTable.getCreationUserField() != null) {
if (this.sqlRow.getObject(sqlTable.getCreationUserField().getName()) == null || this.sqlRow.getObject(sqlTable.getCreationDateField().getName()) == null) {
setFieldValue(this.sqlRow, sqlTable.getCreationUserField(), false, userId);
setFieldValue(this.sqlRow, sqlTable.getCreationDateField(), false, now);
}
}
if (sqlTable.getModifUserField() != null) {
setFieldValue(this.sqlRow, sqlTable.getModifUserField(), false, userId);
setFieldValue(this.sqlRow, sqlTable.getModifDateField(), false, now);
}
}
 
static private boolean setFieldValue(final SQLRowValues vals, final SQLField f, final boolean remove, final Object val) {
if (f == null)
321,17 → 333,6
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
 
@Override
public LightUIElement convert(JSONObject json) {
return new LightEditFrame(json);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightEditFrame(this);
}
362,4 → 363,33
this.editMode = EditMode.READONLY;
}
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
if (this.editMode.equals(EditMode.CREATION)) {
out.writeByte(0);
} else if (this.editMode.equals(EditMode.MODIFICATION)) {
out.writeByte(1);
} else if (this.editMode.equals(EditMode.READONLY)) {
out.writeByte(2);
} else {
throw new IllegalStateException("unknown mode " + this.editMode);
}
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
int mode = in.readByte();
if (mode == 0) {
this.editMode = EditMode.CREATION;
} else if (mode == 1) {
this.editMode = EditMode.MODIFICATION;
} else if (mode == 2) {
this.editMode = EditMode.READONLY;
} else {
throw new IllegalStateException("unknown mode " + mode);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/CompareUtils.java
312,4 → 312,12
}
return null;
}
 
static public final <T extends Comparable<? super T>> T min(final T o1, final T o2) {
return o1.compareTo(o2) < 0 ? o1 : o2;
}
 
static public final <T extends Comparable<? super T>> T max(final T o1, final T o2) {
return o1.compareTo(o2) < 0 ? o2 : o1;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/ExceptionUtils.java
81,7 → 81,7
}
 
static public String getStackTrace(Throwable cause) {
final StringWriter res = new StringWriter(128);
final StringWriter res = new StringWriter(8192);
final PrintWriter pw = new PrintWriter(res);
try {
cause.printStackTrace(pw);
/trunk/OpenConcerto/src/org/openconcerto/utils/ReflectUtils.java
13,6 → 13,7
package org.openconcerto.utils;
 
import java.beans.Expression;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
29,7 → 30,10
public final class ReflectUtils {
 
/**
* Find a constructor with compatible parameters.
* Find a constructor with compatible parameters. NOTE: This follow a simpler algorithm than the
* one in the JLS (<a href=
* "https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2.5">15.12.2.5.
* Choosing the Most Specific Method</a>).
*
* @param cls the class to find a constructor for, not <code>null</code>.
* @param parameterTypes types of parameters the constructor must accept.
58,6 → 62,35
}
}
 
@SuppressWarnings("unchecked")
static public final <T> T createInstance(final Class<T> clazz, Object... args) throws Exception {
// too difficult to order superclasses and all implemented interfaces of args in order to
// find the most specific constructor
return (T) new Expression(clazz, "new", args).getValue();
}
 
/**
* Return the class with the passed name if it is a subclass of <code>clazz</code>.
*
* @param name the name of the class, not <code>null</code>.
* @param clazz the superclass, not <code>null</code>.
* @return the requested class, <code>null</code> if {@link ClassNotFoundException not found} or
* not a {@link Class#asSubclass(Class) subclass} of <code>clazz</code>.
*/
static public final <T> Class<? extends T> getSubclass(final String name, final Class<T> clazz) {
return getSubclass(name, clazz, clazz.getClassLoader());
}
 
static public final <T> Class<? extends T> getSubclass(final String name, final Class<T> clazz, final ClassLoader cl) {
final Class<?> res;
try {
res = Class.forName(name, true, cl);
} catch (ClassNotFoundException e) {
return null;
}
return clazz.isAssignableFrom(res) ? res.asSubclass(clazz) : null;
}
 
static private Map<Type, Type> resolveTypes(Class<?> c, Class<?> raw) {
final Map<Type, Type> res = new HashMap<Type, Type>();
if (!raw.isAssignableFrom(c))
/trunk/OpenConcerto/src/org/openconcerto/utils/FileUtils.java
38,6 → 38,7
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
97,6 → 98,9
}
 
public static void openFile(File f) throws IOException {
if (!f.exists()) {
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
}
if (Desktop.isDesktopSupported()) {
Desktop d = Desktop.getDesktop();
if (d.isSupported(Desktop.Action.OPEN)) {
616,6 → 620,10
return readUTF8(new FileInputStream(f));
}
 
public static final String readUTF8(Path p) throws IOException {
return new String(Files.readAllBytes(p), StandardCharsets.UTF_8);
}
 
public static final String readUTF8(InputStream ins) throws IOException {
return read(ins, StringUtils.UTF8);
}
900,6 → 908,9
* @throws IOException if the file can't be opened.
*/
public static final void open(File f, String[] executables) throws IOException {
if (!f.exists()) {
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
}
try {
openNative(f);
} catch (IOException exn) {
/trunk/OpenConcerto/src/org/openconcerto/utils/ProcessStreams.java
19,6 → 19,7
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.ProcessBuilder.Redirect;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
35,6 → 36,10
static public enum Action {
/**
* Redirect process streams to ours.
*
* @deprecated use {@link ProcessStreams#redirect(ProcessBuilder)} (or
* {@link Redirect#INHERIT} directly) as it makes sure that
* {@link Process#waitFor()} only returns once all streams are flushed.
*/
REDIRECT,
/**
52,6 → 57,10
DO_NOTHING
}
 
static public final ProcessBuilder redirect(final ProcessBuilder pb) throws IOException {
return pb.redirectErrorStream(true).redirectOutput(Redirect.INHERIT);
}
 
static public final Process handle(final Process p, final Action action) throws IOException {
if (action == Action.CLOSE) {
p.getInputStream().close();
/trunk/OpenConcerto/src/org/openconcerto/utils/ExceptionHandler.java
25,6 → 25,7
import java.awt.Desktop.Action;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
47,8 → 48,10
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
66,7 → 69,6
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;
 
/**
* Allow to display an exception both on the GUI and on the console.
81,6 → 83,9
private static boolean showProbably = false;
private static IFactory<String> softwareInfos = null;
private static ThrowableHandler tHandler = null;
private static boolean safeToExit = false;
private static final CompletableFuture<Boolean> FALSE_FUTURE = CompletableFuture.completedFuture(Boolean.FALSE);
private static final CompletableFuture<Boolean> TRUE_FUTURE = CompletableFuture.completedFuture(Boolean.TRUE);
 
public static void setThrowableHandler(ThrowableHandler handler) {
tHandler = handler;
90,11 → 95,19
forumURL = url;
}
 
public synchronized static void setShowProbably(boolean showProbably) {
public static void setSafeToExit(boolean b) {
safeToExit = b;
}
 
public static void setSubmitErrorAutoEnabled(boolean b) {
submitErrorAuto = b;
}
 
public static synchronized void setShowProbably(boolean showProbably) {
ExceptionHandler.showProbably = showProbably;
}
 
public synchronized static boolean isShowProbably() {
public static synchronized boolean isShowProbably() {
return ExceptionHandler.showProbably;
}
 
120,21 → 133,25
* @param comp the modal parent of the error window.
* @param msg the message to display.
* @param originalExn the cause, can be <code>null</code>.
* @return an exception.
* @return a future completed when the error is handled (e.g. the user clicked on the dialog),
* <code>false</code> if the error couldn't be displayed to the user.
*/
static public ExceptionHandler handle(Component comp, String msg, Throwable originalExn) {
static public Future<Boolean> handle(Component comp, String msg, Throwable originalExn) {
final Future<Boolean> res;
if (tHandler != null && tHandler.handle(msg, originalExn)) {
return new ExceptionHandler(msg, originalExn);
res = TRUE_FUTURE;
} else {
res = new ExceptionHandler(comp, msg, originalExn, false).display();
}
return new ExceptionHandler(comp, msg, originalExn, false);
 
assert res != null;
return res;
}
 
static public RuntimeException handle(String msg, Throwable originalExn) {
static public Future<Boolean> handle(String msg, Throwable originalExn) {
return handle(null, msg, originalExn);
}
 
static public RuntimeException handle(String msg) {
static public Future<Boolean> handle(String msg) {
return handle(msg, null);
}
 
147,7 → 164,9
* @return an exception.
*/
static public RuntimeException die(String msg, Throwable originalExn) {
return new ExceptionHandler(null, msg, originalExn);
final ExceptionHandler res = new ExceptionHandler(null, msg, originalExn);
res.display();
return res;
}
 
static public RuntimeException die(String msg) {
160,27 → 179,31
 
// the comp on which to display the popup, may be null
private final Component comp;
private final Future<?> future;
private final boolean quit;
protected static AtomicInteger openedWindows = new AtomicInteger(0);
private static boolean forceUI;
private static boolean submitErrorAuto = false;
 
public static void setForceUI(boolean forceUI) {
ExceptionHandler.forceUI = forceUI;
}
 
private Future<?> display(final boolean error) {
private Future<Boolean> display() {
final boolean error = this.quit;
final String msg = this.getMessage();
// write out the message as soon as possible
getLogger().log(error ? Level.SEVERE : Level.INFO, null, this);
// then show it to the user
if (!GraphicsEnvironment.isHeadless() || forceUI) {
if (openedWindows.get() > 3) {
return FALSE_FUTURE;
}
final FutureTask<Boolean> run = new FutureTask<>(() -> {
return showMsgHardened(msg, error);
});
if (SwingUtilities.isEventDispatchThread()) {
showMsgHardened(msg, error);
run.run();
} else {
final FutureTask<?> run = new FutureTask<Object>(new Runnable() {
public void run() {
showMsgHardened(msg, error);
}
}, null);
if (error) {
try {
SwingUtilities.invokeAndWait(run);
191,19 → 214,16
} else {
SwingUtilities.invokeLater(run);
}
}
return run;
}
return TRUE_FUTURE;
}
return null;
}
 
public final Future<?> getDialogFuture() {
return this.future;
}
 
protected final void showMsgHardened(final String msg, final boolean error) {
protected final Boolean showMsgHardened(final String msg, final boolean error) {
try {
showMsg(msg, error);
return Boolean.TRUE;
} catch (Throwable e) {
// sometimes the VM cannot display the dialog, in that case don't crash the EDT as the
// message has already been logged. Further if this class is used in
217,6 → 237,7
// nothing
}
}
return Boolean.FALSE;
}
 
protected final void showMsg(final String msg, final boolean quit) {
320,62 → 341,6
}
}));
 
final javax.swing.Action submitAction = new AbstractAction("Soumettre l'erreur") {
@Override
public void actionPerformed(ActionEvent e) {
submitError(p, textArea);
}
 
private void submitError(final JPanel p, final JTextComponent textArea) {
final Charset cs = StringUtils.UTF8;
try {
ProductInfo productInfo = ProductInfo.getInstance();
 
String name = "", version = "";
if (productInfo != null) {
name = productInfo.getName();
version = productInfo.getProperty(ProductInfo.VERSION, version);
}
 
final Map<Info, String> systemInfos = SystemInfo.get(false);
final String os = systemInfos.remove(Info.OS);
final String java = systemInfos.toString();
final String encodedData = "java=" + PercentEncoder.encode(java, cs) + "&os=" + PercentEncoder.encode(os, cs) + "&software=" + PercentEncoder.encode(name + version, cs) + "&stack="
+ PercentEncoder.encode(computeSoftwareInformations() + "\n\n" + textArea.getText(), cs);
final String request = "http://bugreport.ilm-informatique.fr:5000/bugreport";
final URL url = new URL(request);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("charset", cs.name());
final byte[] bytes = encodedData.getBytes(cs);
connection.setRequestProperty("Content-Length", String.valueOf(bytes.length));
 
final OutputStream outputStream = connection.getOutputStream();
outputStream.write(bytes);
outputStream.flush();
 
// Get the response
final StringBuilder answer = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
answer.append(line);
}
outputStream.close();
reader.close();
connection.disconnect();
 
JOptionPane.showMessageDialog(p, "Merci d'avoir envoyé le rapport d'erreur au service technique.\nIl sera analysé prochainement.");
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
 
btnPanel.add(new JButton(submitAction));
 
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.EAST;
p.add(btnPanel, c);
400,9 → 365,10
message = msg + "\n\n" + message;
}
message += "\n";
message +=
 
getTrace();
message += getTrace();
if (submitErrorAuto) {
submitError(message);
}
textArea.setText(message);
textArea.setEditable(false);
 
420,9 → 386,22
c.fill = GridBagConstraints.NONE;
c.weighty = 0;
c.insets = new Insets(2, 4, 2, 4);
JPanel closePanel = new JPanel();
closePanel.setLayout(new FlowLayout());
if (safeToExit) {
closePanel.add(new JButton(new AbstractAction("Quitter") {
 
@Override
public void actionPerformed(ActionEvent e) {
System.exit(2);
}
}));
}
final JButton buttonClose = new JButton("Fermer");
p.add(buttonClose, c);
closePanel.add(buttonClose);
 
p.add(closePanel, c);
 
final Window window = this.comp == null ? null : SwingUtilities.getWindowAncestor(this.comp);
final JDialog f;
if (window instanceof Frame) {
432,7 → 411,7
}
f.setContentPane(p);
f.pack();
f.setSize(580, 680);
f.setSize(780, 580);
f.setMinimumSize(new Dimension(380, 380));
f.setLocationRelativeTo(this.comp);
final ActionListener al = new ActionListener() {
439,12 → 418,12
 
@Override
public void actionPerformed(ActionEvent e) {
openedWindows.decrementAndGet();
if (quit) {
System.exit(1);
} else {
f.dispose();
}
 
}
};
buttonClose.addActionListener(al);
453,10 → 432,13
@Override
public void windowClosing(WindowEvent e) {
al.actionPerformed(null);
 
}
});
openedWindows.incrementAndGet();
 
f.setVisible(true);
 
}
 
private String getTrace() {
485,17 → 467,70
private ExceptionHandler(Component comp, String msg, Throwable cause, boolean quit) {
super(msg, cause);
this.comp = comp;
this.future = this.display(quit);
this.quit = quit;
}
 
public ExceptionHandler(String msg, Throwable cause) {
super(msg, cause);
this.comp = null;
this.future = null;
private void submitError(String error) {
final Charset cs = StringUtils.UTF8;
try {
ProductInfo productInfo = ProductInfo.getInstance();
 
String name = "", version = "";
if (productInfo != null) {
name = productInfo.getName();
version = productInfo.getProperty(ProductInfo.VERSION, version);
}
 
final Map<Info, String> systemInfos = SystemInfo.get(false);
final String os = systemInfos.remove(Info.OS);
final String java = systemInfos.toString();
final String encodedData = "java=" + PercentEncoder.encode(java, cs) + "&os=" + PercentEncoder.encode(os, cs) + "&software=" + PercentEncoder.encode(name + version, cs) + "&stack="
+ PercentEncoder.encode(computeSoftwareInformations() + "\n\n" + error, cs);
Thread t = new Thread(new Runnable() {
 
@Override
public void run() {
final String request = "http://bugreport.ilm-informatique.fr:5000/bugreport";
try {
System.err.println("ExceptionHandler.submitError");
final URL url = new URL(request);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("charset", cs.name());
final byte[] bytes = encodedData.getBytes(cs);
connection.setRequestProperty("Content-Length", String.valueOf(bytes.length));
 
final OutputStream outputStream = connection.getOutputStream();
outputStream.write(bytes);
outputStream.flush();
 
// Get the response
final StringBuilder answer = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
answer.append(line);
}
outputStream.close();
reader.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start();
 
} catch (Exception ex) {
ex.printStackTrace();
}
}
 
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
ExceptionHandler.handle("Fichier de configuration corrompu\n\nmulti\nline", new IllegalStateException("Id manquant"));
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/Grammar_es.java
New file
0,0 → 1,123
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.i18n;
 
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
 
import net.jcip.annotations.Immutable;
 
@Immutable
public class Grammar_es extends Grammar {
 
static private final Grammar_es INSTANCE = new Grammar_es();
 
public static Grammar_es getInstance() {
return INSTANCE;
}
 
private Grammar_es() {
this(Locale.forLanguageTag("es"));
}
 
protected Grammar_es(final Locale l) {
super(l);
}
 
@Override
protected Collection<? extends VariantKey> createVariantKeys() {
return Arrays.asList(SINGULAR, PLURAL, INDEFINITE_ARTICLE_SINGULAR, INDEFINITE_ARTICLE_PLURAL, DEFINITE_ARTICLE_SINGULAR, DEFINITE_ARTICLE_PLURAL, DEMONSTRATIVE_SINGULAR, DEMONSTRATIVE_PLURAL,
INDEFINITE_NUMERAL, DEFINITE_NUMERAL, DEMONSTRATIVE_NUMERAL, INDEFINITE_ORDINAL, DEFINITE_ORDINAL);
}
 
@Override
protected Collection<? extends NounClass> createNounClasses() {
return Arrays.asList(NounClass.FEMININE, NounClass.MASCULINE);
}
 
public final Phrase createPhrase(final String singular) {
return this.createPhrase(singular, null);
}
 
public final Phrase createPhrase(final String singular, final String plural) {
final Phrase res = new Phrase(this, singular, null);
if (plural != null)
res.putVariant(PLURAL, plural);
return res;
}
 
@Override
public String getVariant(Phrase noun, VariantKey key) {
final String res;
if (key.equals(SINGULAR)) {
res = noun.getBase();
} else if (key.equals(INDEFINITE_ARTICLE_SINGULAR)) {
res = (noun.getNounClass() == NounClass.FEMININE ? "una " : "un ") + getSingular(noun);
} else if (key.equals(DEFINITE_ARTICLE_SINGULAR)) {
res = getDefiniteArticle(noun) + getSingular(noun);
} else if (key.equals(DEMONSTRATIVE_SINGULAR)) {
res = (noun.getNounClass() == NounClass.FEMININE ? "esta " : "este ") + getSingular(noun);
} else if (key.equals(PLURAL)) {
res = getPlural(noun.getBase());
} else if (key.equals(INDEFINITE_ARTICLE_PLURAL)) {
res = (noun.getNounClass() == NounClass.FEMININE ? "unas " : "unos ") + getPlural(noun);
} else if (key.equals(DEFINITE_ARTICLE_PLURAL)) {
res = (noun.getNounClass() == NounClass.FEMININE ? "las " : "los ") + getPlural(noun);
} else if (key.equals(DEMONSTRATIVE_PLURAL)) {
res = (noun.getNounClass() == NounClass.FEMININE ? "estas " : "estos ") + getPlural(noun);
} else if (key.equals(INDEFINITE_NUMERAL)) {
res = "{0, plural, =0 {" + getZero(noun) + "} one {# " + getSingular(noun) + "} other {# " + getPlural(noun) + "}}";
} else if (key.equals(DEFINITE_NUMERAL)) {
res = "{0, plural, =0 {" + getZero(noun) + "} one {" + getVariant(noun, DEFINITE_ARTICLE_SINGULAR) + "} other {" + (noun.getNounClass() == NounClass.FEMININE ? "las " : "los ") + " # "
+ getPlural(noun) + "}}";
} else if (key.equals(DEMONSTRATIVE_NUMERAL)) {
res = "{0, plural, =0 {" + getZero(noun) + "} one {" + getVariant(noun, DEMONSTRATIVE_SINGULAR) + "} other {" + (noun.getNounClass() == NounClass.FEMININE ? "estas " : "estos ") + " # "
+ getPlural(noun) + "}}";
} else if (key.equals(DEFINITE_ORDINAL) || key.equals(INDEFINITE_ORDINAL)) {
final boolean estFéminin = noun.getNounClass() == NounClass.FEMININE;
final String article = key.equals(DEFINITE_ORDINAL) ? (estFéminin ? "la " : "el ") : "";
res = article + "{0, ordinal, %digits-ordinal" + (estFéminin ? "-feminine" : "") + "} " + getSingular(noun);
} else {
res = null;
}
 
return res;
}
 
protected String getZero(Phrase noun) {
return (noun.getNounClass() == NounClass.MASCULINE ? "ningún " : "ninguna ") + noun.getBase();
}
 
protected String getDefiniteArticle(Phrase noun) {
if (noun.getNounClass() == NounClass.MASCULINE || noun.getBase().startsWith("a") || noun.getBase().startsWith("ha"))
return "el ";
else
return "la ";
}
 
protected String getSingular(Phrase noun) {
final String res = noun.getVariant(SINGULAR);
return res == null ? noun.getBase() : res;
}
 
protected String getPlural(Phrase noun) {
final String res = noun.getVariant(PLURAL);
return res == null ? getPlural(noun.getBase()) : res;
}
 
protected String getPlural(String noun) {
return noun + 's';
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/Grammar_fr.java
84,11 → 84,11
} else if (key.equals(DEMONSTRATIVE_PLURAL)) {
res = "ces " + getPlural(noun);
} else if (key.equals(INDEFINITE_NUMERAL)) {
res = "{0, plural, =0 {auc" + getVariant(noun, INDEFINITE_ARTICLE_SINGULAR) + "} one {# " + getSingular(noun) + "} other {# " + getPlural(noun) + "}}";
res = "{0, plural, =0 {auc" + noun.getVariant(INDEFINITE_ARTICLE_SINGULAR) + "} one {# " + getSingular(noun) + "} other {# " + getPlural(noun) + "}}";
} else if (key.equals(DEFINITE_NUMERAL)) {
res = "{0, plural, =0 {auc" + getVariant(noun, INDEFINITE_ARTICLE_SINGULAR) + "} one {" + getVariant(noun, DEFINITE_ARTICLE_SINGULAR) + "} other {les # " + getPlural(noun) + "}}";
res = "{0, plural, =0 {auc" + noun.getVariant(INDEFINITE_ARTICLE_SINGULAR) + "} one {" + noun.getVariant(DEFINITE_ARTICLE_SINGULAR) + "} other {les # " + getPlural(noun) + "}}";
} else if (key.equals(DEMONSTRATIVE_NUMERAL)) {
res = "{0, plural, =0 {auc" + getVariant(noun, INDEFINITE_ARTICLE_SINGULAR) + "} one {" + getVariant(noun, DEMONSTRATIVE_SINGULAR) + "} other {ces # " + getPlural(noun) + "}}";
res = "{0, plural, =0 {auc" + noun.getVariant(INDEFINITE_ARTICLE_SINGULAR) + "} one {" + noun.getVariant(DEMONSTRATIVE_SINGULAR) + "} other {ces # " + getPlural(noun) + "}}";
} else if (key.equals(DEFINITE_ORDINAL) || key.equals(INDEFINITE_ORDINAL)) {
final boolean estFéminin = noun.getNounClass() == NounClass.FEMININE;
final String article = key.equals(DEFINITE_ORDINAL) ? (estFéminin ? "la " : "le ") : "";
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_fr.properties
7,7 → 7,7
 
memory=mémoire
megabytes={0} Mo
processors={0} processor{0,choice,1#|1<s}
processors={0, plural, one { # processeur } other { # processeurs } }
os=Système d''exploitation
javaVersion=Version <b>{0}</b> de {1}
javaHome=dossier d'installation
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_pl.properties
New file
0,0 → 1,29
true_key=prawdziwe
false_key=fa\u0142szywy
yes_key=tak
no_key=Nie
 
linkOpenError=Error while opening {0}
 
memory=pami\u0119ci
megabytes={0} MB
processors={0, plural, one { # procesora } other { # procesorów } }
os=Operating system
javaVersion=Version <b>{0}</b> of {1}
javaHome=installation directory
no.laf=No look and feel
 
user=u\u017Cytkownika
home.dir=home directory
cwd=current directory
 
network=Network
hardwareAddress=adres sprz\u0119towy
interfaceFullName=full name
interfaceState=state
interfaceStateUp=up
interfaceStateDown=down
 
cut=Wytnij
copy=Kopi\u0119
paste=Wklej
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_en.properties
7,7 → 7,7
 
memory=memory
megabytes={0} MB
processors={0} processor{0,choice,1#|1<s}
processors={0, plural, one { # processor } other { # processors } }
os=Operating system
javaVersion=Version <b>{0}</b> of {1}
javaHome=installation directory
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/LocalizedInstances.java
14,6 → 14,7
package org.openconcerto.utils.i18n;
 
import org.openconcerto.utils.Log;
import org.openconcerto.utils.ReflectUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Value;
 
88,19 → 89,20
// test emptiness to not mix languages
for (Locale targetLocale = locale; targetLocale != null && l.isEmpty(); targetLocale = this.cntrl.getFallbackLocale(baseName, targetLocale)) {
localeRes = targetLocale;
// e.g. "fr_FR", "fr"
for (final Locale candidate : this.cntrl.getCandidateLocales(baseName, targetLocale)) {
// e.g. org.acme.MyClass_fr
final String bundleName = this.cntrl.toBundleName(baseName, candidate);
 
// code first
final Class<?> loadedClass = loadClass(bundleName, cl);
if (loadedClass != null && this.clazz.isAssignableFrom(loadedClass)) {
final Class<? extends T> subclazz = loadedClass.asSubclass(this.clazz);
final Class<? extends T> subclazz = ReflectUtils.getSubclass(bundleName, this.clazz, cl.getClassLoader());
if (subclazz != null) {
try {
final Value<? extends T> instance = this.getInstance(subclazz);
if (instance.hasValue())
l.add(instance.getValue());
else
Log.get().warning(loadedClass + " exists but the constructor wasn't found");
Log.get().warning(subclazz + " exists but the constructor wasn't found");
} catch (Exception e) {
Log.get().log(Level.WARNING, "Couldn't create an instance using " + subclazz, e);
}
118,14 → 120,6
return Tuple2.create(localeRes, l);
}
 
private final Class<?> loadClass(final String name, final Class<?> cl) {
try {
return Class.forName(name, true, cl.getClassLoader());
} catch (ClassNotFoundException e) {
return null;
}
}
 
/**
* The no-arg static method to use.
*
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/TM.java
21,6 → 21,7
import java.beans.Introspector;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
28,6 → 29,7
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
49,9 → 51,37
public class TM {
 
static public enum MissingMode {
EXCEPTION, NULL, STRING
EXCEPTION {
@Override
protected String returnMissing(TM tm, String key) throws MissingResourceException {
throw new MissingResourceException("Missing translation", tm.getBaseName(), key);
}
},
NULL {
@Override
protected String returnMissing(TM tm, String key) {
return null;
}
},
STRING {
@Override
protected String returnMissing(TM tm, String key) {
return '!' + key + '!';
}
};
 
protected abstract String returnMissing(final TM tm, final String key) throws MissingResourceException;
 
// method to avoid array allocation and Arrays.toString()
protected final String returnResult(final TM tm, final String res, final String key) throws MissingResourceException {
return res == null ? this.returnMissing(tm, key) : res;
}
 
protected final String returnResult(final TM tm, final String res, final String... keys) throws MissingResourceException {
return res == null ? this.returnMissing(tm, Arrays.toString(keys)) : res;
}
}
 
static public final String NOUN_CLASS_PROP = "nounClass";
static {
assert NOUN_CLASS_PROP.equals(Introspector.decapitalize(NounClass.class.getSimpleName()));
178,21 → 208,40
}
 
public final String translate(final MissingMode mode, final String key, final Object... args) throws MissingResourceException {
return translate(mode, key, new MessageArgs(args));
return translate(mode, key, args.length == 0 ? MessageArgs.getEmpty() : new MessageArgs(args));
}
 
public final String translateFirst(final MissingMode mode, final String... keys) throws MissingResourceException {
return translateFirst(mode, MessageArgs.getEmpty(), keys);
}
 
/**
* Return the first non-<code>null</code> result.
*
* @param mode what to do if all keys are <code>null</code>.
* @param args the arguments.
* @param keys the keys to search for.
* @return the first non-<code>null</code> result.
* @throws MissingResourceException if {@link MissingMode#EXCEPTION} and all keys are
* <code>null</code>.
*/
public final String translateFirst(final MissingMode mode, final MessageArgs args, final String... keys) throws MissingResourceException {
String res = null;
for (int i = 0; i < keys.length && res == null; i++) {
final String key = keys[i];
if (key != null)
res = this.translate(MissingMode.NULL, key, args);
}
return mode.returnResult(this, res, keys);
}
 
private final String translate(final MissingMode mode, final String key, MessageArgs args) throws MissingResourceException {
Objects.requireNonNull(mode, "Null mode");
Objects.requireNonNull(key, "Null key");
Objects.requireNonNull(args, "Null arguments");
final String res = this.translations.translate(key, args);
if (res == null) {
if (mode == MissingMode.STRING)
return '!' + key + '!';
else if (mode == MissingMode.NULL)
return null;
else
throw new MissingResourceException("Missing translation", this.getBaseName(), key);
return mode.returnResult(this, res, key);
}
return res;
}
 
protected MessageArgs replaceMap(final MessageArgs args, final String msg) {
final MessageArgs res;
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/Grammar_pl.java
New file
0,0 → 1,55
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.i18n;
 
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
 
import net.jcip.annotations.Immutable;
 
@Immutable
public class Grammar_pl extends Grammar {
 
static private final Grammar_pl INSTANCE = new Grammar_pl();
 
public static Grammar_pl getInstance() {
return INSTANCE;
}
 
private Grammar_pl() {
this(Locale.forLanguageTag("pl"));
}
 
protected Grammar_pl(final Locale l) {
super(l);
}
 
@Override
protected Collection<? extends VariantKey> createVariantKeys() {
return Arrays.asList(SINGULAR, PLURAL, INDEFINITE_ARTICLE_SINGULAR, INDEFINITE_ARTICLE_PLURAL, DEFINITE_ARTICLE_SINGULAR, DEFINITE_ARTICLE_PLURAL, DEMONSTRATIVE_SINGULAR, DEMONSTRATIVE_PLURAL,
INDEFINITE_NUMERAL, DEFINITE_NUMERAL, DEMONSTRATIVE_NUMERAL, INDEFINITE_ORDINAL, DEFINITE_ORDINAL);
}
 
@Override
protected Collection<? extends NounClass> createNounClasses() {
return Arrays.asList(NounClass.FEMININE, NounClass.MASCULINE, NounClass.NEUTER);
}
 
@Override
public String getVariant(Phrase noun, VariantKey key) {
// TODO
return noun.getBase();
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/Phrase.java
18,17 → 18,18
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
 
import com.ibm.icu.text.MessageFormat;
import com.ibm.icu.text.MessagePattern;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
import com.ibm.icu.text.MessageFormat;
import com.ibm.icu.text.MessagePattern;
 
/**
* A phrase and its declension. E.g. "light bulb or electrical outlet" and
* "light bulbs or electrical outlets".
* A phrase and its declension. E.g. "light bulb or electrical outlet" and "light bulbs or
* electrical outlets".
*
* @author Sylvain
* @see <a href="Wikipedia">http://en.wikipedia.org/wiki/Declension</a>
46,7 → 47,7
@GuardedBy("this")
private final Map<Object, String> variants;
@GuardedBy("this")
private final Set<Object> explicitVariants;
private final Set<VariantKey> explicitVariants;
 
public Phrase(Grammar grammar, String base, NounClass nounClass) {
super();
61,7 → 62,7
} else {
this.variants = new HashMap<Object, String>();
this.variants.put(null, this.getBase());
this.explicitVariants = new HashSet<Object>();
this.explicitVariants = new HashSet<>();
}
}
 
88,6 → 89,22
return this.putVariant(key, variant, true);
}
 
/**
* Put a variant only if needed, i.e. if {@link #getVariant(VariantKey)} doesn't already return
* <code>variant</code>. This is useful to keep {@link #getExplicitVariants()} to a minimum.
*
* @param key which variant, e.g. plural.
* @param variant the value, e.g. feet.
* @return <code>true</code> if the variant was put.
*/
public final synchronized boolean putVariantIfDifferent(final VariantKey key, final String variant) {
final boolean diff = !variant.equals(this.getVariant(key));
if (diff) {
this.putVariant(key, variant);
}
return diff;
}
 
private final synchronized String putVariant(final VariantKey key, final String variant, final boolean explicit) {
final String res = this.variants.put(key, variant);
if (explicit) {
99,6 → 116,16
}
 
/**
* The variants that have been explicitly set by {@link #putVariant(VariantKey, String)}, as
* opposed to variants computed automatically by the {@link #getGrammar() grammar}.
*
* @return all explicit variants.
*/
public synchronized Set<VariantKey> getExplicitVariants() {
return this.explicitVariants == null ? null : new HashSet<>(this.explicitVariants);
}
 
/**
* Get a variant. If the asked variant wasn't put by {@link #putVariant(VariantKey, String)},
* the {@link #getGrammar() grammar} is {@link Grammar#getVariant(Phrase, VariantKey) used}.
*
140,6 → 167,31
}
 
@Override
public synchronized int hashCode() {
return Objects.hash(this.base, this.explicitVariants, this.grammar, this.nounClass);
}
 
@Override
public synchronized boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Phrase o = (Phrase) obj;
final boolean fast = Objects.equals(this.base, o.base) && Objects.equals(this.explicitVariants, o.explicitVariants) && Objects.equals(this.grammar, o.grammar)
&& Objects.equals(this.nounClass, o.nounClass);
if (!fast || this.variants == null)
return fast;
for (final VariantKey e : this.explicitVariants) {
if (!Objects.equals(this.variants.get(e), o.variants.get(e)))
return false;
}
return true;
}
 
@Override
public String toString() {
final String cl = this.getNounClass() == null ? " " : " (" + this.getNounClass().getName() + ") ";
final String gr = this.getGrammar() == null ? "" : " with " + this.getGrammar();
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/MessageArgs.java
13,15 → 13,15
package org.openconcerto.utils.i18n;
 
import java.util.HashMap;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeSet;
 
import com.ibm.icu.text.MessageFormat;
 
import net.jcip.annotations.ThreadSafe;
 
import com.ibm.icu.text.MessageFormat;
 
/**
* {@link MessageFormat} can take numbered or named arguments, this class unifies both. This class
* is thread safe if the array or map isn't modified.
33,6 → 33,16
return m instanceof LinkedHashMap;
}
 
static private final MessageArgs EMPTY = new MessageArgs(new Object[0]);
 
public final static MessageArgs getEmpty() {
return EMPTY;
}
 
public final static MessageArgs create(final String key, final Object value) {
return new MessageArgs(Collections.singletonMap(key, value));
}
 
private final boolean mapPrimary;
private Object[] array;
private Map<String, ?> map;
82,12 → 92,16
protected synchronized Map<String, ?> getMap() {
if (this.map == null) {
final int stop = this.array.length;
final Map<String, Object> res = new HashMap<String, Object>(stop);
if (stop == 0) {
this.map = Collections.emptyMap();
} else {
final Map<String, Object> res = new LinkedHashMap<>(stop);
for (int i = 0; i < stop; i++) {
res.put(String.valueOf(i), this.array[i]);
}
this.map = res;
this.map = Collections.unmodifiableMap(res);
}
}
return this.map;
}
 
/trunk/OpenConcerto/src/org/openconcerto/utils/mime/MimeType.java
New file
0,0 → 1,205
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
/*
* Copyright 2007-2009 Medsea Business Solutions S.L.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.openconcerto.utils.mime;
 
import java.io.Serializable;
import java.util.regex.Pattern;
 
/**
* This class represents a simple MimeType object. A mime type is made up of two parts
* <code>&lt;media type&gt;/&lt;sub type&gt;</code>. The media type can be something like
* <code>application</code> or <code>text</code> and the the sub type can be something like
* <code>xml</code> or <code>plain</code>.
*
* Both the media type and sub type can also be the wild card <code>*</code> such as
* <code>*&#47;*</code> and <code>text&#47;*</code>. Note, if the media type is the wild card then
* the sub type must also be a wild card.
*
* @author Steven McArdle
*
*/
public class MimeType implements Serializable {
 
private static final long serialVersionUID = -1324243127744494894L;
 
private static final Pattern mimeSplitter = Pattern.compile("[/;]++");
 
protected String mediaType = "*";
protected String subType = "*";
 
// This is a estimate of how specific this mime type is
private int specificity = 1;
 
/**
* Construct a MimeType from another MimeType instance
*
* @param mimeType
*/
public MimeType(final MimeType mimeType) {
this.mediaType = mimeType.mediaType;
this.subType = mimeType.subType;
this.specificity = mimeType.specificity;
}
 
/**
* Construct a mime type from a String such as <code>text/plain</code>. It tries to ensure that
* the mime type pattern passed in is correctly formatted.
*
* @param mimeType
* @throws IllegalArgumentException
*/
public MimeType(final String mimeType) throws IllegalArgumentException {
if (mimeType == null || mimeType.trim().length() == 0) {
throw new IllegalArgumentException("Invalid MimeType [" + mimeType + "]");
}
String[] parts = mimeSplitter.split(mimeType.trim());
 
if (parts.length > 0) {
// Treat as the mediaType
mediaType = getValidMediaType(parts[0]);
}
if (parts.length > 1) {
subType = getValidSubType(parts[1]);
}
}
 
/**
* Get the media type part of the mime type.
*
* @return media type
*/
public String getMediaType() {
return mediaType;
}
 
/**
* Get the sub type of the mime type
*
* @return sub type
*/
public String getSubType() {
return subType;
}
 
/**
* See if this MimeType is the same as the passed in mime type string
*
* @param mimeType as a String
* @return true if the MimeType passed in has the same media and sub types, else returns false.
*/
private boolean match(final String mimeType) {
return toString().equals(mimeType);
}
 
/**
* Get the hashCode of this MimeType. The hashCode is calculate as (31 * mediaType.hashCode()) +
* subType.hashCode()
*
* @return calculated hashCode
* @see Object#hashCode()
*/
public int hashCode() {
return (31 * mediaType.hashCode()) + subType.hashCode();
}
 
/**
* Overrides the equals method of <code>java.lang.Object</code>. This is able to compare against
* another MimeType instance or a string representation of a mime type.
*
* @return true if the types match else false.
* @see Object#equals(Object o)
*/
public boolean equals(Object o) {
if (o instanceof MimeType) {
if (this.mediaType.equals(((MimeType) o).mediaType) && this.subType.equals(((MimeType) o).subType)) {
return true;
}
} else if (o instanceof String) {
return match((String) o);
}
return false;
}
 
/**
* Overrides the toString method of <code>java.lang.Object</code>.
*
* @return String representation i.e. <code>&lt;media type&gt;/&lt;sub type&gt;.
* @see Object#toString()
*/
public String toString() {
return mediaType + "/" + subType;
}
 
/**
* This indicates how specific the mime types is i.e. how good a match the mime type is when
* returned from the getMimeTypes(...) calls.
* <p>
* This is calculated by the number of times this MimeType would be returned if the Collection
* was not normalised. The higher the count the more MimeDetectors have matched this type. As
* this can be a false positive for types such as application/octect-stream and text/plain where
* they would be returned by multiple MimeDetector(s). These types are referred to as root mime
* types where ALL mime types derive from application/octet-stream and all text/* types derive
* from text/plan so in these cases we set the specificity to 0 no matter how many times they
* match. This ensures they are regarded as the least specific in the returned Collection.
* </p>
*
* @return how specific this MimeType is according to the rest of the MimeTypes in a Collection.
*/
public int getSpecificity() {
return specificity;
}
 
/*
* Set the value of the specificity. The higher the value the more specific a MimeType is.
*/
void setSpecificity(final int specificity) {
this.specificity = specificity;
}
 
/*
* Check the media type at least looks valid. TODO: Enforce more rigorous checking of valid
* media types.
*/
private String getValidMediaType(final String mediaType) {
if (mediaType == null || mediaType.trim().length() == 0) {
return "*";
}
return mediaType;
}
 
/*
* Check the sub type at least looks valid. TODO: Enforce more rigorous checking of valid sub
* types.
*/
private String getValidSubType(final String subType) {
if (subType == null || subType.trim().length() == 0 || "*".equals(mediaType)) {
// If the mediaType is a wild card then the sub type must also be a wild card
return "*";
}
return subType;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/mime/mime.cache
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/src/org/openconcerto/utils/mime/mime.cache
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/src/org/openconcerto/utils/mime/FreeDesktopMimeDetector.java
New file
0,0 → 1,772
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.mime;
 
/*
* Copyright 2007-2009 Medsea Business Solutions S.L.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.Log;
import org.openconcerto.utils.OSFamily;
import org.openconcerto.utils.StreamUtils;
 
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.spi.FileTypeDetector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Pattern;
 
/**
* <p>
* The Opendesktop shared mime database contains glob rules and magic number lookup information to
* enable applications to detect the mime types of files.
* </p>
* <p>
* This class uses the mime.cache file which is one of the files created by the update-mime-database
* application. This file is a memory mapped file that enables the database to be updated and copied
* without interrupting applications.
* </p>
* <p>
* This implementation follows the memory mapped spec so it is not required to restart an
* application using this mime detector should the underlying mime.cache database change.
* </p>
* <p>
* For a complete description of the information contained in this file please see:
* http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
* </p>
* <p>
* This class also follows, where possible, the RECOMENDED order of detection as detailed in this
* spec. Thanks go to Mathias Clasen at Red Hat for pointing me to the original xdgmime
* implementation http://svn.gnome.org/viewvc/glib/trunk/
* gio/xdgmime/xdgmimecache.c?revision=7784&view=markup
* </p>
* More up to date : https://github.com/GNOME/beagle/blob/master/beagle/glue/xdgmime/xdgmimecache.c
*
* @author Steven McArdle
* @see <a
* href="http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html">Shared
* MIME-Info</a>
*/
@SuppressWarnings({ "unqualified-field-access" })
public class FreeDesktopMimeDetector extends FileTypeDetector {
 
public static enum Mode {
/**
* <a href=
* "http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html#idm140625828606432"
* >Recommended checking order</a>
*/
RECOMMENDED, DATA_ONLY, NAME_ONLY
}
 
public static final String DEFAULT_CACHE = "freeDesktop.mime.cache";
public static final String DEFAULT_MODE = "freeDesktop.mime.mode";
 
static private Mode getDefaultMode() {
final String m = System.getProperty(DEFAULT_MODE);
if (m != null) {
try {
return Mode.valueOf(m.toUpperCase());
} catch (Exception e) {
Log.get().log(Level.CONFIG, "Ignoring invalid mode : " + m, e);
}
}
return Mode.RECOMMENDED;
}
 
private static File mimeCacheFile = OSFamily.getInstance() == OSFamily.Windows ? null : new File("/usr/share/mime/mime.cache");
 
static private boolean canReadFile(final File f, final String msg) {
if (f == null)
return false;
final boolean res = f.isFile() && f.canRead();
if (!res)
Log.get().config(msg + f);
return res;
}
 
static private Object getDefaultCache() {
final String m = System.getProperty(DEFAULT_CACHE);
final File f = m != null ? new File(m) : null;
if (canReadFile(f, "Ignoring invalid passed file : ")) {
return f;
} else if (canReadFile(mimeCacheFile, "Ignoring invalid system file : ")) {
return mimeCacheFile;
} else {
final URL res = FreeDesktopMimeDetector.class.getResource("mime.cache");
if (res == null)
throw new IllegalStateException("No mime.cache found for " + FreeDesktopMimeDetector.class);
return res;
}
}
 
private final ByteBuffer content;
private final Mode mode;
 
public FreeDesktopMimeDetector() throws IOException {
this(getDefaultCache(), getDefaultMode());
}
 
public FreeDesktopMimeDetector(final File mimeCacheFile, final Mode mode) throws IOException {
this((Object) mimeCacheFile, mode);
}
 
public FreeDesktopMimeDetector(final InputStream is, final Mode mode) throws IOException {
this((Object) is, mode);
}
 
// InputStream will be closed
private FreeDesktopMimeDetector(final Object cache, final Mode mode) throws IOException {
if (cache instanceof File || cache instanceof FileInputStream) {
// Map the mime.cache file as a memory mapped file
try (final FileInputStream is = cache instanceof FileInputStream ? (FileInputStream) cache : new FileInputStream((File) cache); final FileChannel rCh = is.getChannel();) {
content = rCh.map(FileChannel.MapMode.READ_ONLY, 0, rCh.size());
}
} else {
final ByteArrayOutputStream out = new ByteArrayOutputStream(250 * 1024);
try (final InputStream is = cache instanceof URL ? ((URL) cache).openStream() : (InputStream) cache) {
StreamUtils.copy(is, out);
}
content = ByteBuffer.wrap(out.toByteArray());
}
this.mode = mode;
}
 
@Override
public String probeContentType(Path path) throws IOException {
final Collection<String> col;
switch (this.mode) {
case RECOMMENDED:
col = this.getMimeTypesFile(path.toFile());
break;
case DATA_ONLY:
try (final InputStream is = new BufferedInputStream(new FileInputStream(path.toFile()))) {
col = this.getMimeTypesInputStream(is);
}
break;
case NAME_ONLY:
col = this.getMimeTypesFileName(path.getFileName().toString());
break;
default:
throw new IllegalStateException("Unknown mode : " + this.mode);
}
return CollectionUtils.getFirst(col);
}
 
/**
* This method resolves mime types closely in accordance with the RECOMENDED order of detection
* detailed in the Opendesktop shared mime database specification
* http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html See
* the Recommended checking order.
*
* @param fileName the name to inspect.
* @return a collection of MIME types.
*/
public Collection<String> getMimeTypesFileName(String fileName) {
Collection<WeightedMimeType> mimeTypes = new ArrayList<WeightedMimeType>();
// Lookup the globbing methods first
lookupMimeTypesForGlobFileName(fileName, mimeTypes);
 
return normalizeWeightedMimeList(mimeTypes);
}
 
/**
* This method resolves mime types closely in accordance with the RECOMENDED order of detection
* detailed in the Opendesktop shared mime database specification
* http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html See
* the Recommended checking order.
*
* @param file the file to inspect.
* @return a collection of MIME types.
* @throws IOException if the file couldn't be read.
*/
public Collection<String> getMimeTypesFile(File file) throws IOException {
Collection<String> mimeTypes = getMimeTypesFileName(file.getName());
if (!file.exists()) {
return mimeTypes;
}
try (final InputStream is = new BufferedInputStream(new FileInputStream(file))) {
return _getMimeTypes(mimeTypes, is);
}
}
 
/**
* This method is unable to perform glob matching as no name is available. This means that it
* does not follow the recommended order of detection defined in the shared mime database spec
* http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
*
* @param in the stream to inspect.s
* @return a collection of MIME types.
* @throws IOException if the stream couldn't be read.
*/
public Collection<String> getMimeTypesInputStream(InputStream in) throws IOException {
return lookupMimeTypesForMagicData(in);
}
 
/**
* This method is unable to perform glob matching as no name is available. This means that it
* does not follow the recommended order of detection defined in the shared mime database spec
* http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
*
* @param data the data to inspect.
* @return a collection of MIME types.
*/
public Collection<String> getMimeTypesByteArray(byte[] data) {
return lookupMagicData(data);
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + " using the mime.cache file version [" + getMajorVersion() + "." + getMinorVersion() + "].";
}
 
public String dump() {
return "{MAJOR_VERSION=" + getMajorVersion() + " MINOR_VERSION=" + getMinorVersion() + " ALIAS_LIST_OFFSET=" + getAliasListOffset() + " PARENT_LIST_OFFSET=" + getParentListOffset()
+ " LITERAL_LIST_OFFSET=" + getLiteralListOffset() + " REVERSE_SUFFIX_TREE_OFFSET=" + getReverseSuffixTreeOffset() + " GLOB_LIST_OFFSET=" + getGlobListOffset() + " MAGIC_LIST_OFFSET="
+ getMagicListOffset() + " NAMESPACE_LIST_OFFSET=" + getNameSpaceListOffset() + " ICONS_LIST_OFFSET=" + getIconListOffset() + " GENERIC_ICONS_LIST_OFFSET="
+ getGenericIconListOffset() + "}";
}
 
private Collection<String> lookupMimeTypesForMagicData(InputStream in) throws IOException {
int offset = 0;
int len = getMaxExtents();
byte[] data = new byte[len];
// Mark the input stream
in.mark(len);
 
try {
// Since an InputStream might return only some data (not all
// requested), we have to read in a loop until
// either EOF is reached or the desired number of bytes have been
// read.
int restBytesToRead = len;
while (restBytesToRead > 0) {
int bytesRead = in.read(data, offset, restBytesToRead);
if (bytesRead < 0)
break; // EOF
 
offset += bytesRead;
restBytesToRead -= bytesRead;
}
} finally {
// Reset the input stream to where it was marked.
in.reset();
}
return lookupMagicData(data);
}
 
private Collection<String> lookupMagicData(byte[] data) {
 
Collection<String> mimeTypes = new ArrayList<String>();
 
int listOffset = getMagicListOffset();
int numEntries = content.getInt(listOffset);
int offset = content.getInt(listOffset + 8);
 
for (int i = 0; i < numEntries; i++) {
final int matchOffset = offset + (16 * i);
final String mimeType = compareToMagicData(matchOffset, data);
if (mimeType != null) {
mimeTypes.add(mimeType);
} else {
final String nonMatch = getMimeType(content.getInt(matchOffset + 4));
mimeTypes.remove(nonMatch);
}
}
 
return mimeTypes;
}
 
private String compareToMagicData(int offset, byte[] data) {
// TODO
// int priority = content.getInt(offset);
int mimeOffset = content.getInt(offset + 4);
int numMatches = content.getInt(offset + 8);
int matchletOffset = content.getInt(offset + 12);
 
for (int i = 0; i < numMatches; i++) {
if (matchletMagicCompare(matchletOffset + (i * 32), data)) {
return getMimeType(mimeOffset);
}
}
return null;
}
 
private boolean matchletMagicCompare(int offset, byte[] data) {
int n_children = content.getInt(offset + 24);
int child_offset = content.getInt(offset + 28);
 
if (magic_matchlet_compare_to_data(offset, data)) {
if (n_children == 0)
return true;
 
for (int i = 0; i < n_children; i++) {
if (matchletMagicCompare(child_offset + 32 * i, data))
return true;
}
}
 
return false;
}
 
private boolean magic_matchlet_compare_to_data(int offset, byte[] data) {
int rangeStart = content.getInt(offset);
int rangeLength = content.getInt(offset + 4);
int dataLength = content.getInt(offset + 12);
int dataOffset = content.getInt(offset + 16);
int maskOffset = content.getInt(offset + 20);
 
for (int i = rangeStart; i <= rangeStart + rangeLength; i++) {
boolean validMatch = true;
if (i + dataLength > data.length) {
return false;
}
if (maskOffset != 0) {
for (int j = 0; j < dataLength; j++) {
if ((content.get(dataOffset + j) & content.get(maskOffset + j)) != (data[j + i] & content.get(maskOffset + j))) {
validMatch = false;
break;
}
}
} else {
for (int j = 0; j < dataLength; j++) {
if (content.get(dataOffset + j) != data[j + i]) {
validMatch = false;
break;
}
}
}
 
if (validMatch) {
return true;
}
}
return false;
}
 
private void lookupGlobLiteral(String fileName, Collection<WeightedMimeType> mimeTypes) {
int listOffset = getLiteralListOffset();
int numEntries = content.getInt(listOffset);
 
int min = 0;
int max = numEntries - 1;
while (max >= min) {
int mid = (min + max) / 2;
String literal = getString(content.getInt((listOffset + 4) + (12 * mid)));
int cmp = literal.compareTo(fileName);
if (cmp < 0) {
min = mid + 1;
} else if (cmp > 0) {
max = mid - 1;
} else {
String mimeType = getMimeType(content.getInt((listOffset + 4) + (12 * mid) + 4));
int weight = content.getInt((listOffset + 4) + (12 * mid) + 8);
mimeTypes.add(new WeightedMimeType(mimeType, literal, weight));
return;
}
}
}
 
private void lookupGlobFileNameMatch(String fileName, Collection<WeightedMimeType> mimeTypes) {
final int listOffset = getGlobListOffset();
final int numEntries = content.getInt(listOffset);
final int entriesOffset = listOffset + 4;
 
for (int i = 0; i < numEntries; i++) {
final int entryOffset = entriesOffset + 12 * 1;
final int offset = content.getInt(entryOffset);
final int mimeTypeOffset = content.getInt(entryOffset + 4);
final int weightNFlags = content.getInt(entryOffset + 8);
final int weight = weightNFlags & 0xFF;
final boolean cs = (weightNFlags & 0x100) != 0;
 
final Pattern pattern = Pattern.compile(getString(offset, true), !cs ? (Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) : 0);
if (pattern.matcher(fileName).matches()) {
final String mimeType = getMimeType(mimeTypeOffset);
final String globPattern = getString(offset, false);
mimeTypes.add(new WeightedMimeType(mimeType, globPattern, weight));
}
}
}
 
private Collection<String> normalizeWeightedMimeList(Collection<WeightedMimeType> weightedMimeTypes) {
if (weightedMimeTypes.isEmpty())
return Collections.emptySet();
Collection<WeightedMimeType> mimeTypes = new LinkedHashSet<WeightedMimeType>();
 
// Sort the weightedMimeTypes
Collections.sort((List<WeightedMimeType>) weightedMimeTypes, new Comparator<WeightedMimeType>() {
public int compare(WeightedMimeType obj1, WeightedMimeType obj2) {
return obj1.weight - obj2.weight;
}
});
 
// Keep only globs with the biggest weight. They are in weight order at
// this point
int weight = 0;
int patternLen = 0;
for (final WeightedMimeType mw : weightedMimeTypes) {
if (weight < mw.weight) {
weight = mw.weight;
}
if (weight >= mw.weight) {
if (mw.pattern.length() > patternLen) {
patternLen = mw.pattern.length();
}
mimeTypes.add(mw);
}
}
 
// Now keep only the longest patterns
for (final WeightedMimeType mw : weightedMimeTypes) {
if (mw.pattern.length() < patternLen) {
mimeTypes.remove(mw);
}
}
 
// Could possibly have multiple mimeTypes here with the same weight and
// pattern length. Can even have multiple entries for the same type so
// lets remove
// any duplicates by copying entries to a HashSet that can only have a
// single instance
// of each type
Collection<String> _mimeTypes = new HashSet<String>();
for (final WeightedMimeType mw : mimeTypes) {
_mimeTypes.add(mw.toString());
}
return _mimeTypes;
}
 
private void lookupMimeTypesForGlobFileName(String fileName, Collection<WeightedMimeType> mimeTypes) {
if (fileName == null) {
return;
}
 
lookupGlobLiteral(fileName, mimeTypes);
if (!mimeTypes.isEmpty()) {
return;
}
 
int len = fileName.length();
lookupGlobSuffix(fileName, false, len, mimeTypes);
if (mimeTypes.isEmpty()) {
lookupGlobSuffix(fileName, true, len, mimeTypes);
}
 
if (mimeTypes.isEmpty()) {
lookupGlobFileNameMatch(fileName, mimeTypes);
}
}
 
private void lookupGlobSuffix(String fileName, boolean ignoreCase, int len, Collection<WeightedMimeType> mimeTypes) {
int listOffset = getReverseSuffixTreeOffset();
int numEntries = content.getInt(listOffset);
int offset = content.getInt(listOffset + 4);
 
lookupGlobNodeSuffix(fileName, numEntries, offset, ignoreCase, len, mimeTypes, new StringBuffer());
}
 
private void lookupGlobNodeSuffix(final String fileName, final int numEntries, final int offset, final boolean ignoreCase, int len, Collection<WeightedMimeType> mimeTypes, StringBuffer pattern) {
final char character = ignoreCase ? fileName.toLowerCase().charAt(len - 1) : fileName.charAt(len - 1);
 
if (character == 0) {
return;
}
 
int min = 0;
int max = numEntries - 1;
while (max >= min && len >= 0) {
int mid = (min + max) / 2;
 
char matchChar = (char) content.getInt(offset + (12 * mid));
if (ignoreCase)
matchChar = Character.toLowerCase(matchChar);
if (matchChar < character) {
min = mid + 1;
} else if (matchChar > character) {
max = mid - 1;
} else {
len--;
// first leaf nodes (matchChar==0) then tree nodes
final int numChildren = content.getInt(offset + (12 * mid) + 4);
final int firstChildOffset = content.getInt(offset + (12 * mid) + 8);
if (len > 0) {
pattern.append(matchChar);
lookupGlobNodeSuffix(fileName, numChildren, firstChildOffset, ignoreCase, len, mimeTypes, pattern);
}
// if the name did not match a longer pattern, try to match this one
if (mimeTypes.isEmpty()) {
for (int i = 0; i < numChildren; i++) {
final int childOffset = firstChildOffset + (12 * i);
if (content.getInt(childOffset) != 0) {
// not a leaf node anymore
break;
}
 
final int mimeOffset = content.getInt(childOffset + 4);
final int weightNFlags = content.getInt(childOffset + 8);
final int weight = weightNFlags & 0xFF;
final boolean cs = (weightNFlags & 0x100) != 0;
 
if (!(cs && ignoreCase))
mimeTypes.add(new WeightedMimeType(getMimeType(mimeOffset), pattern.toString(), weight));
}
}
return;
}
}
}
 
static class WeightedMimeType extends MimeType {
 
private static final long serialVersionUID = 1L;
String pattern;
int weight;
 
WeightedMimeType(String mimeType, String pattern, int weight) {
super(mimeType);
this.pattern = pattern;
this.weight = weight;
}
}
 
private int getMaxExtents() {
return content.getInt(getMagicListOffset() + 4);
}
 
private String aliasLookup(String alias) {
int aliasListOffset = getAliasListOffset();
int min = 0;
int max = content.getInt(aliasListOffset) - 1;
 
while (max >= min) {
int mid = (min + max) / 2;
// content.position((aliasListOffset + 4) + (mid * 8));
 
int aliasOffset = content.getInt((aliasListOffset + 4) + (mid * 8));
int mimeOffset = content.getInt((aliasListOffset + 4) + (mid * 8) + 4);
 
int cmp = getMimeType(aliasOffset).compareTo(alias);
if (cmp < 0) {
min = mid + 1;
} else if (cmp > 0) {
max = mid - 1;
} else {
return getMimeType(mimeOffset);
}
}
return null;
}
 
private String unaliasMimeType(String mimeType) {
String lookup = aliasLookup(mimeType);
return lookup == null ? mimeType : lookup;
}
 
private boolean isMimeTypeSubclass(String mimeType, String subClass) {
String umimeType = unaliasMimeType(mimeType);
String usubClass = unaliasMimeType(subClass);
MimeType _mimeType = new MimeType(umimeType);
MimeType _subClass = new MimeType(usubClass);
 
if (umimeType.compareTo(usubClass) == 0) {
return true;
}
 
if (isSuperType(usubClass) && (_mimeType.getMediaType().equals(_subClass.getMediaType()))) {
return true;
}
 
// Handle special cases text/plain and application/octet-stream
if (usubClass.equals("text/plain") && _mimeType.getMediaType().equals("text")) {
return true;
}
 
if (usubClass.equals("application/octet-stream")) {
return true;
}
int parentListOffset = getParentListOffset();
int numParents = content.getInt(parentListOffset);
int min = 0;
int max = numParents - 1;
while (max >= min) {
int med = (min + max) / 2;
int offset = content.getInt((parentListOffset + 4) + (8 * med));
String parentMime = getMimeType(offset);
int cmp = parentMime.compareTo(umimeType);
if (cmp < 0) {
min = med + 1;
} else if (cmp > 0) {
max = med - 1;
} else {
offset = content.getInt((parentListOffset + 4) + (8 * med) + 4);
int _numParents = content.getInt(offset);
for (int i = 0; i < _numParents; i++) {
int parentOffset = content.getInt((offset + 4) + (4 * i));
if (isMimeTypeSubclass(getMimeType(parentOffset), usubClass)) {
return true;
}
}
break;
}
}
return false;
}
 
private boolean isSuperType(String mimeType) {
return mimeType.endsWith("/*");
}
 
private int getGenericIconListOffset() {
return content.getInt(36);
}
 
private int getIconListOffset() {
return content.getInt(32);
}
 
private int getNameSpaceListOffset() {
return content.getInt(28);
}
 
private int getMagicListOffset() {
return content.getInt(24);
}
 
private int getGlobListOffset() {
return content.getInt(20);
}
 
private int getReverseSuffixTreeOffset() {
return content.getInt(16);
}
 
private int getLiteralListOffset() {
return content.getInt(12);
}
 
private int getParentListOffset() {
return content.getInt(8);
}
 
private int getAliasListOffset() {
return content.getInt(4);
}
 
private short getMinorVersion() {
return content.getShort(2);
}
 
private short getMajorVersion() {
return content.getShort(0);
}
 
private String getMimeType(int offset) {
return getString(offset);
}
 
private String getString(int offset) {
return getString(offset, false);
}
 
private String getString(int offset, boolean regularExpression) {
int position = content.position();
content.position(offset);
StringBuffer buf = new StringBuffer();
char c = 0;
while ((c = (char) content.get()) != 0) {
if (regularExpression) {
switch (c) {
case '.':
buf.append("\\");
break;
case '*':
case '+':
case '?':
buf.append(".");
}
}
buf.append(c);
}
// Reset position
content.position(position);
 
if (regularExpression) {
buf.insert(0, '^');
buf.append('$');
}
return buf.toString();
}
 
private Collection<String> _getMimeTypes(Collection<String> mimeTypes, final InputStream in) throws IOException {
if (mimeTypes.isEmpty() || mimeTypes.size() > 1) {
Collection<String> _mimeTypes = getMimeTypesInputStream(in);
 
if (!_mimeTypes.isEmpty()) {
if (!mimeTypes.isEmpty()) {
// more than one glob matched
 
// Check for same mime type
for (final String mimeType : mimeTypes) {
if (_mimeTypes.contains(mimeType)) {
// mimeTypes = new ArrayList();
mimeTypes.add(mimeType);
// return mimeTypes;
}
// Check for mime type subtype
for (final String _mimeType : _mimeTypes) {
if (isMimeTypeSubclass(mimeType, _mimeType)) {
// mimeTypes = new ArrayList();
mimeTypes.add(mimeType);
// return mimeTypes;
}
}
}
} else {
// No globs matched but we have magic matches
return _mimeTypes;
}
}
}
 
return mimeTypes;
 
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/mime/File.java
New file
0,0 → 1,35
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.mime;
 
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
 
public class File {
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println(File.class.getName() + " file...");
System.out.println("Allow to find the MIME type of files");
System.out.println("The '" + FreeDesktopMimeDetector.DEFAULT_MODE + "' property can be set to " + Arrays.asList(FreeDesktopMimeDetector.Mode.values()));
System.out.println("\tthe default is the first one, see http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html#idm140625828606432");
System.exit(1);
}
for (final String f : args) {
final Path p = Paths.get(f);
System.out.println(f + '\t' + new FreeDesktopMimeDetector().probeContentType(p));
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/mime/FileMagicMimeDetector.java
New file
0,0 → 1,39
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.mime;
 
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.spi.FileTypeDetector;
 
/**
* <p>
* The <a
* href="http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html"
* >freedesktop Shared MIME-Info</a> only contains basic magic support. In particular it misses
* indirect offset and calculations that <a href="http://www.darwinsys.com/file">file</a> supports.
* For example these features are needed for <a
* href="https://github.com/file/file/blob/master/magic/Magdir/msooxml">MS OOXML</a>.
* </p>
*
* @see <a href="https://github.com/file/file/blob/master/doc/magic.man">Magic Format</a>
*/
public class FileMagicMimeDetector extends FileTypeDetector {
 
@Override
public String probeContentType(Path path) throws IOException {
throw new UnsupportedOperationException();
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/utils/SleepingQueue.java
18,6 → 18,7
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Collection;
import java.util.Deque;
import java.util.concurrent.Callable;
227,6 → 228,13
}
 
public final void start() {
this.start(null);
}
 
public final void start(final IClosure<Thread> customizeThread) {
customizeThread(this.tasksQueue);
if (customizeThread != null)
customizeThread.executeChecked(this.tasksQueue);
synchronized (this) {
this.tasksQueue.start();
this.setState(RunningState.RUNNING);
338,14 → 346,25
}
 
/**
* An exception was thrown by a task. This implementation merely
* {@link Exception#printStackTrace()}.
* An exception was thrown by a task. This implementation uses
* {@link Thread#getUncaughtExceptionHandler()} or
* {@link Thread#getDefaultUncaughtExceptionHandler()} if available, otherwise falls back to
* just {@link Exception#printStackTrace()}. To set the handler, {@link #start(IClosure)} can be
* used.
*
* @param exn the exception thrown.
*/
protected void exceptionThrown(final ExecutionException exn) {
final Thread thr = this.tasksQueue;
UncaughtExceptionHandler h = thr.getUncaughtExceptionHandler();
if (h == null)
h = Thread.getDefaultUncaughtExceptionHandler();
if (h != null) {
h.uncaughtException(thr, exn);
} else {
exn.printStackTrace();
}
}
 
/**
* Cancel all queued tasks and the current task.
617,7 → 636,6
private final class SingleThreadedExecutor extends DropperQueue<FutureTask<?>> {
private SingleThreadedExecutor() {
super(SleepingQueue.this.name + System.currentTimeMillis());
customizeThread(this);
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/utils/StreamUtils.java
13,11 → 13,9
package org.openconcerto.utils;
 
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
24,6 → 22,9
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.logging.Level;
 
public class StreamUtils {
 
48,6 → 49,7
* @throws IOException if an error occurs while reading or writing.
*/
public static void copy(InputStream in, OutputStream out) throws IOException {
// TODO use in.transferTo(out) in Java 9
copy(in, out, 512 * 1024);
}
 
56,6 → 58,8
}
 
public static long copy(InputStream in, OutputStream out, final int bufferSize, final long length) throws IOException {
if (bufferSize < 1)
throw new IllegalArgumentException("Buffer size too small : " + bufferSize);
final byte[] buffer = new byte[bufferSize];
long totalCount = 0;
final boolean copyAll = length < 0;
62,10 → 66,14
while (copyAll || totalCount < length) {
final long toRead = copyAll ? buffer.length : Math.min(length - totalCount, buffer.length);
// since buffer.length is an int
assert 0 <= toRead && toRead <= Integer.MAX_VALUE;
assert 0 < toRead && toRead <= Integer.MAX_VALUE;
final int count = in.read(buffer, 0, (int) toRead);
if (count == -1)
if (count <= 0) {
// like Files.copy(InputStream, OutputStream), stop if reading 0 bytes
if (count == 0)
Log.get().log(Level.WARNING, "", new IllegalStateException("read() returned 0 for " + in));
break;
}
totalCount += count;
out.write(buffer, 0, count);
}
75,14 → 83,8
}
 
public static void copy(InputStream ins, File out) throws IOException {
// buffered since read() in copy(InputStream, OutputStream) may return 1 byte at a time
final OutputStream ous = new BufferedOutputStream(new FileOutputStream(out));
try {
copy(ins, ous);
} finally {
ous.close();
Files.copy(ins, out.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
 
/**
* Read until the end of the stream is reached. NOTE : since this method didn't create the
/trunk/OpenConcerto/src/org/openconcerto/utils/ntp/SNTPClient.java
New file
0,0 → 1,116
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.ntp;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
 
public class SNTPClient {
 
protected final DatagramSocket socket;
 
/**
* The SNTP time is referenced to 01/01/1900-00:00. On the other hand, Unix systems and Java
* reference time to 01/01/1970-00:00. This means that convertion is necessary.
*/
private static final long SECS_1900_1970 = 2208988800L;
 
/**
* Constructor.
*
* @param timeout the response timeout (in milliseconds).
* @throws IllegalArgumentException if the timeout is invalid.
* @throws SocketException if an error occurs while creating the socket.
*/
public SNTPClient() throws SocketException {
this.socket = new DatagramSocket();
// 10s timeout
socket.setSoTimeout(10000);
}
 
/**
* Retrieves the network time offset from an SNTP server.
*
* @param host the server host address (IP or DNS).
* @param port the server port.
* @return the network time offset (in milliseconds).
*
* @throws IOException if an error occurs while contacting the server.
*/
public long getOffsetInMillis(final String host, final int port) throws IOException {
final InetAddress addr = InetAddress.getByName(host);
// Send
final Message smessage = new Message();
smessage.setTransmitTimestamp(toTimestamp(System.currentTimeMillis()));
final ByteArrayOutputStream output = new ByteArrayOutputStream();
smessage.encodeMessage(output);
final byte[] data = output.toByteArray();
output.close();
 
DatagramPacket rpacket = new DatagramPacket(data, data.length, addr, port);
socket.send(rpacket);
 
// Receive
final DatagramPacket packet = new DatagramPacket(new byte[Message.MAXIMUM_LENGTH], Message.MAXIMUM_LENGTH);
socket.receive(packet);
final long destinationTime = System.currentTimeMillis();
final Message rmessage = Message.decodeMessage(new ByteArrayInputStream(packet.getData()));
final long originalTime = fromTimestamp(rmessage.getOriginateTimestamp());
final long receiveTime = fromTimestamp(rmessage.getReceiveTimestamp());
final long transmitTime = fromTimestamp(rmessage.getTransmitTimestamp());
return ((receiveTime - originalTime) + (transmitTime - destinationTime)) / 2;
 
}
 
public void close() {
socket.close();
}
 
/**
* Converts Java time to an SNTP timestamp.
*
* @param time the Java time (in milliseconds).
* @return the SNTP timestamp.
*/
private static Timestamp toTimestamp(final long time) {
final double temp = ((double) time) / 1000D;
final long integer = (long) Math.floor(temp);
final long fraction = (long) ((temp - (double) integer) * 0x100000000L);
return new Timestamp(integer + SECS_1900_1970, fraction);
}
 
/**
* Converts an SNTP timestamp to Java time.
*
* @param timestamp the timestamp.
* @return the Java time (in milliseconds).
*/
private static final long fromTimestamp(final Timestamp timestamp) {
long time = (timestamp.getInteger() - SECS_1900_1970) * 1000L;
time += (timestamp.getFraction() * 1000L) / 0x100000000L;
return time;
}
 
public static void main(String[] args) throws IOException {
final SNTPClient client = new SNTPClient();
final long offset = client.getOffsetInMillis("fr.pool.ntp.org", 123);
client.close();
System.out.println(offset);
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/ntp/Timestamp.java
New file
0,0 → 1,36
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.ntp;
 
public final class Timestamp {
 
public static final Timestamp ZERO = new Timestamp(0, 0);
 
private final long integer;
private final long fraction;
 
public Timestamp(final long integer, final long fraction) {
this.integer = integer;
this.fraction = fraction;
}
 
public long getInteger() {
return integer;
}
 
public long getFraction() {
return fraction;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/utils/ntp/Message.java
New file
0,0 → 1,448
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils.ntp;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
 
public final class Message {
 
/** Maximum message length (in bytes), without authentication */
public static final int MAXIMUM_LENGTH = 384;
 
/** Leap Indicator. */
private byte byLeapIndicator;
 
/** Version Number. */
private byte byVersionNumber = 0x04;
 
/** Client mode. */
private byte byMode = 0x03;
 
/** Stratum. */
private byte byStratum;
 
/** Poll Interval. */
private byte byPollInterval;
 
/** Precision. */
private byte byPrecision;
 
/** Rood Delay. */
private double dRootDelay;
 
/** Root Dispersion. */
private double dRootDispersion;
 
/** Reference Identifier. */
private byte[] sReferenceIdentifier = "LOCL".getBytes();
 
/** Reference Timestamp. */
private Timestamp tReferenceTimestamp = Timestamp.ZERO;
 
/** Originate Timestamp. */
private Timestamp tOriginateTimestamp = Timestamp.ZERO;
 
/** Receive Timestamp. */
private Timestamp tReceiveTimestamp = Timestamp.ZERO;
 
/** Transmit Timestamp. */
private Timestamp tTransmitTimestamp = Timestamp.ZERO;
 
/**
* Returns the Leap Indicator.
*
* @return the Leap Indicator.
*/
public byte getLeapIndicator() {
return byLeapIndicator;
}
 
/**
* Sets the Leap Indicator.
*
* @param byLeapIndicator the Leap Indicator.
*/
public void setLeapIndicator(final byte byLeapIndicator) {
this.byLeapIndicator = byLeapIndicator;
}
 
/**
* Returns the Version Number.
*
* @return the Version Number.
*/
public byte getVersionNumber() {
return byVersionNumber;
}
 
/**
* Sets the Version Number.
*
* @param byVersionNumber the Version Number.
*/
public void setVersionNumber(final byte byVersionNumber) {
this.byVersionNumber = byVersionNumber;
}
 
/**
* Returns the Mode.
*
* @return the Mode.
*/
public byte getMode() {
return byMode;
}
 
/**
* Sets the Mode.
*
* @param byMode the Mode.
*/
public void setMode(final byte byMode) {
this.byMode = byMode;
}
 
/**
* Returns the Stratum.
*
* @return the Stratum.
*/
public byte getStratum() {
return byStratum;
}
 
/**
* Sets the Stratum.
*
* @param byStratum the Stratum.
*/
public void setStratum(final byte byStratum) {
this.byStratum = byStratum;
}
 
/**
* Returns the Poll Interval.
*
* @return the Poll Interval.
*/
public byte getPollInterval() {
return byPollInterval;
}
 
/**
* Sets the Poll Interval.
*
* @param byPollInterval the Poll Interval.
*/
public void setPollInterval(final byte byPollInterval) {
this.byPollInterval = byPollInterval;
}
 
/**
* Returns the Precision.
*
* @return the Precision.
*/
public byte getPrecision() {
return byPrecision;
}
 
/**
* Sets the Precision.
*
* @param byPrecision the Precision.
*/
public void setPrecision(final byte byPrecision) {
this.byPrecision = byPrecision;
}
 
/**
* Returns the Root Delay.
*
* @return the Root Delay.
*/
public double getRootDelay() {
return dRootDelay;
}
 
/**
* Sets the Root Delay.
*
* @param dRootDelay the Root Delay.
*/
public void setRootDelay(final double dRootDelay) {
this.dRootDelay = dRootDelay;
}
 
/**
* Returns the Root Dispersion.
*
* @return the Root Dispersion.
*/
public double getRootDispersion() {
return dRootDispersion;
}
 
/**
* Sets the Root Dispersion.
*
* @param dRootDispersion the Root Dispersion.
*/
public void setRootDispersion(final double dRootDispersion) {
this.dRootDispersion = dRootDispersion;
}
 
/**
* Returns the Reference Identifier.
*
* @return the Reference Identifier.
*/
public byte[] getReferenceIdentifier() {
return sReferenceIdentifier;
}
 
/**
* Sets the Reference Identifier.
*
* @param sReferenceIdentifier the Reference Identifier.
*/
public void setReferenceIdentifier(final byte[] sReferenceIdentifier) {
this.sReferenceIdentifier = sReferenceIdentifier;
}
 
/**
* Returns the Reference Timestamp.
*
* @return the Reference Timestamp.
*/
public Timestamp getReferenceTimestamp() {
return tReferenceTimestamp;
}
 
/**
* Sets the Reference Timestamp.
*
* @param tReferenceTimestamp the Reference Timestamp.
*/
public void setReferenceTimestamp(final Timestamp tReferenceTimestamp) {
this.tReferenceTimestamp = tReferenceTimestamp;
}
 
/**
* Returns the Originate Timestamp.
*
* @return the Originate Timestamp.
*/
public Timestamp getOriginateTimestamp() {
return tOriginateTimestamp;
}
 
/**
* Sets the Originate Timestamp.
*
* @param tOriginateTimestamp the Originate Timestamp.
*/
public void setOriginateTimestamp(final Timestamp tOriginateTimestamp) {
this.tOriginateTimestamp = tOriginateTimestamp;
}
 
/**
* Returns the Receive Timestamp.
*
* @return the Receive Timestamp.
*/
public Timestamp getReceiveTimestamp() {
return tReceiveTimestamp;
}
 
/**
* Sets the Receive Timestamp.
*
* @param tReceiveTimestamp the Receive Timestamp.
*/
public void setReceiveTimestamp(final Timestamp tReceiveTimestamp) {
this.tReceiveTimestamp = tReceiveTimestamp;
}
 
/**
* Returns the Transmit Timestamp.
*
* @return the Transmit Timestamp.
*/
public Timestamp getTransmitTimestamp() {
return tTransmitTimestamp;
}
 
/**
* Sets the Transmit Timestamp.
*
* @param tTransmitTimestamp the Transmit Timestamp.
*/
public void setTransmitTimestamp(final Timestamp tTransmitTimestamp) {
this.tTransmitTimestamp = tTransmitTimestamp;
}
 
/**
* Encodes an SNTP message to a byte stream.
*
* @param output the byte stream.
*/
public void encodeMessage(final OutputStream output) throws IOException {
byte flags = (byte) (this.getLeapIndicator() << 6);
flags += (byte) (this.getVersionNumber() << 3);
flags += this.getMode();
output.write(flags);
output.write(this.getStratum());
output.write(this.getPollInterval());
output.write(this.getPrecision());
encodeFixedPoint(this.getRootDelay(), output);
encodeFixedPoint(this.getRootDispersion(), output);
encodeBitstring(this.getReferenceIdentifier(), output);
encodeTimestamp(this.getReferenceTimestamp(), output);
encodeTimestamp(this.getOriginateTimestamp(), output);
encodeTimestamp(this.getReceiveTimestamp(), output);
encodeTimestamp(this.getTransmitTimestamp(), output);
}
 
/**
* Decodes an SNTP message from a byte stream.
*
* @param input the byte stream.
* @return the message.
*/
public static Message decodeMessage(final InputStream input) throws IOException {
final Message message = new Message();
final byte flags = (byte) input.read();
message.setLeapIndicator((byte) (flags >> 6));
message.setVersionNumber((byte) ((flags >> 3) & 0x07));
message.setMode((byte) (flags & 0x07));
message.setStratum((byte) input.read());
message.setPollInterval((byte) input.read());
message.setPrecision((byte) input.read());
message.setRootDelay(decodeFixedPoint(input));
message.setRootDispersion(decodeFixedPoint(input));
message.setReferenceIdentifier(decodeBitstring(input));
message.setReferenceTimestamp(decodeTimestamp(input));
message.setOriginateTimestamp(decodeTimestamp(input));
message.setReceiveTimestamp(decodeTimestamp(input));
message.setTransmitTimestamp(decodeTimestamp(input));
 
return message;
}
 
/**
* Encodes a 32 bit number to a byte stream.
*
* @param number the number to encode.
* @param output the byte stream.
* @throws IOException if an error occurs while writting to the stream.
*/
private static void encode32(final long number, final OutputStream output) throws IOException {
for (int i = 3; i >= 0; i--) {
output.write((int) ((number >> (8 * i)) & 0xFF));
}
}
 
/**
* Decodes a 32 bit number from a byte stream.
*
* @param input the byte stream.
* @return the decoded number.
* @throws IOException if an error occurs while reading from the stream.
*/
private static long decode32(final InputStream input) throws IOException {
long number = 0;
for (int i = 0; i < 4; i++) {
number = (number << 8) + input.read();
}
return number;
}
 
/**
* Encodes a 32-bit bitstring to a byte stream.
*
* @param bitstring the bitstring to encode.
* @param output the byte stream.
* @throws IOException if an error occurs while writting to the stream.
*/
private static void encodeBitstring(final byte[] bitstring, final OutputStream output) throws IOException {
final byte[] temp = { 0, 0, 0, 0 };
System.arraycopy(bitstring, 0, temp, 0, bitstring.length);
output.write(temp);
}
 
/**
* Decodes a 32-bit bitstring from a byte stream.
*
* @param input the byte stream.
* @return the decoded string.
* @throws IOException if an error occurs while reading from the stream.
*/
private static byte[] decodeBitstring(final InputStream input) throws IOException {
final byte[] bitstring = new byte[4];
input.read(bitstring, 0, 4);
 
return bitstring;
}
 
/**
* Encodes a 32 bit fixed-point number to a byte stream.
*
* @param number the fixed-point number to encode.
* @param output the byte stream.
* @throws IOException if an error occurs while writting to the stream.
*/
private static void encodeFixedPoint(final double number, final OutputStream output) throws IOException {
encode32((long) (number * 0x10000L), output);
}
 
/**
* Decodes a 32 bit fixed-point number from a byte stream. The binary point is between bits 15
* and 16.
*
* @param input the byte stream.
* @return the decoded fixed-point number.
* @throws IOException if an error occurs while reading from the stream.
*/
private static double decodeFixedPoint(final InputStream input) throws IOException {
return ((double) decode32(input)) / 0x10000L;
}
 
/**
* Encodes a timestamp to a byte stream.
*
* @param timestamp the timestamp to encode.
* @param output the byte stream.
* @throws IOException if an error occurs while writting to the stream.
*/
private static void encodeTimestamp(final Timestamp timestamp, final OutputStream output) throws IOException {
encode32(timestamp.getInteger(), output);
encode32(timestamp.getFraction(), output);
}
 
/**
* Decodes a timestamp from a byte stream.
*
* @param input the byte stream.
* @return the decoded timestamp.
* @throws IOException if an error occurs while reading from the stream.
*/
private static Timestamp decodeTimestamp(final InputStream input) throws IOException {
final long integer = decode32(input);
final long fraction = decode32(input);
return new Timestamp(integer, fraction);
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/TableSorter.java
37,6 → 37,8
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
45,6 → 47,8
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
 
import net.jcip.annotations.Immutable;
 
/**
* TableSorter is a decorator for TableModels; adding sorting functionality to a supplied
* TableModel. TableSorter does not store or copy the data in its TableModel; instead it maintains a
51,16 → 55,18
* map from the row indexes of the view to the row indexes of the model. As requests are made of the
* sorter (like getValueAt(row, col)) they are passed to the underlying model after the row numbers
* have been translated via the internal mapping array. This way, the TableSorter appears to hold
* another copy of the table with the rows in a different order. <p/>TableSorter registers itself as
* a listener to the underlying model, just as the JTable itself would. Events recieved from the
* model are examined, sometimes manipulated (typically widened), and then passed on to the
* TableSorter's listeners (typically the JTable). If a change to the model has invalidated the
* order of TableSorter's rows, a note of this is made and the sorter will resort the rows the next
* time a value is requested. <p/>When the tableHeader property is set, either by using the
* setTableHeader() method or the two argument constructor, the table header may be used as a
* complete UI for TableSorter. The default renderer of the tableHeader is decorated with a renderer
* that indicates the sorting status of each column. In addition, a mouse listener is installed with
* the following behavior:
* another copy of the table with the rows in a different order.
* <p/>
* TableSorter registers itself as a listener to the underlying model, just as the JTable itself
* would. Events recieved from the model are examined, sometimes manipulated (typically widened),
* and then passed on to the TableSorter's listeners (typically the JTable). If a change to the
* model has invalidated the order of TableSorter's rows, a note of this is made and the sorter will
* resort the rows the next time a value is requested.
* <p/>
* When the tableHeader property is set, either by using the setTableHeader() method or the two
* argument constructor, the table header may be used as a complete UI for TableSorter. The default
* renderer of the tableHeader is decorated with a renderer that indicates the sorting status of
* each column. In addition, a mouse listener is installed with the following behavior:
* <ul>
* <li>Mouse-click: Clears the sorting status of all other columns and advances the sorting status
* of that column through three values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED
72,8 → 78,9
* column do not cancel the statuses of columns that are already sorting - giving a way to initiate
* a compound sort.
* </ul>
* <p/>This is a long overdue rewrite of a class of the same name that first appeared in the swing
* table demos in 1997.
* <p/>
* This is a long overdue rewrite of a class of the same name that first appeared in the swing table
* demos in 1997.
*
* @author Philip Milne
* @author Brendon McLean
92,11 → 99,13
private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
 
public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((Comparable) o1).compareTo(o2);
}
};
public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
public static final Comparator<Object> LEXICAL_COMPARATOR = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
}
108,8 → 117,8
private JTableHeader tableHeader;
private MouseListener mouseListener;
private TableModelListener tableModelListener;
private Map columnComparators = new HashMap();
private List sortingColumns = new ArrayList();
private Map<Class<?>, Comparator<?>> columnComparators = new HashMap<>();
private final List<Directive> sortingColumns = new ArrayList<>();
 
private boolean enabled;
private boolean sorting;
144,6 → 153,9
}
 
public void setTableModel(TableModel tableModel) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
if (this.tableModel != null) {
this.tableModel.removeTableModelListener(tableModelListener);
}
188,6 → 200,9
}
 
public void setTableHeader(JTableHeader tableHeader) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
if (this.tableHeader != null) {
this.tableHeader.removeMouseListener(mouseListener);
TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
207,9 → 222,22
return sortingColumns.size() != 0;
}
 
public final List<Directive> getSortingColumns() {
return Collections.unmodifiableList(this.sortingColumns);
}
 
public void setSortingColumns(List<Directive> sortingColumns) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
this.sortingColumns.clear();
this.sortingColumns.addAll(sortingColumns);
sortingStatusChanged();
}
 
private Directive getDirective(int column) {
for (int i = 0; i < sortingColumns.size(); i++) {
Directive directive = (Directive) sortingColumns.get(i);
Directive directive = sortingColumns.get(i);
if (directive.column == column) {
return directive;
}
234,6 → 262,9
}
 
private synchronized void sortingStatusChanged() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
this.sortingStatusChanged(true);
}
 
271,7 → 302,7
sortingStatusChanged(fire);
}
 
public void setColumnComparator(Class type, Comparator comparator) {
public void setColumnComparator(Class<?> type, Comparator<?> comparator) {
if (comparator == null) {
columnComparators.remove(type);
} else {
280,8 → 311,8
}
 
protected Comparator getComparator(int column) {
Class columnType = tableModel.getColumnClass(column);
Comparator comparator = (Comparator) columnComparators.get(columnType);
Class<?> columnType = tableModel.getColumnClass(column);
Comparator<?> comparator = columnComparators.get(columnType);
if (comparator != null) {
return comparator;
}
292,6 → 323,9
}
 
private Row[] getViewToModel() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
if (viewToModel == null) {
int tableModelRowCount = tableModel.getRowCount();
viewToModel = new Row[tableModelRowCount];
318,6 → 352,9
}
 
private int[] getModelToView() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
if (modelToView == null) {
int n = getViewToModel().length;
modelToView = new int[n];
334,31 → 371,50
 
// TableModel interface methods
 
@Override
public int getRowCount() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
return (tableModel == null) ? 0 : tableModel.getRowCount();
}
 
@Override
public int getColumnCount() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
return (tableModel == null) ? 0 : tableModel.getColumnCount();
}
 
@Override
public String getColumnName(int column) {
return tableModel.getColumnName(column);
}
 
public Class getColumnClass(int column) {
@Override
public Class<?> getColumnClass(int column) {
return tableModel.getColumnClass(column);
}
 
@Override
public boolean isCellEditable(int row, int column) {
return tableModel.isCellEditable(modelIndex(row), column);
}
 
@Override
public Object getValueAt(int row, int column) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
return tableModel.getValueAt(modelIndex(row), column);
}
 
@Override
public void setValueAt(Object aValue, int row, int column) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("must be called from EDT");
}
tableModel.setValueAt(aValue, modelIndex(row), column);
}
 
376,7 → 432,7
 
// Helper classes
 
private class Row implements Comparable {
private class Row implements Comparable<Row> {
private int modelIndex;
 
public Row(int index) {
383,12 → 439,13
this.modelIndex = index;
}
 
public int compareTo(Object o) {
@Override
public int compareTo(Row o) {
int row1 = modelIndex;
int row2 = ((Row) o).modelIndex;
int row2 = o.modelIndex;
 
for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
Directive directive = (Directive) it.next();
for (Iterator<Directive> it = sortingColumns.iterator(); it.hasNext();) {
Directive directive = it.next();
int column = directive.column;
Object o1 = tableModel.getValueAt(row1, column);
Object o2 = tableModel.getValueAt(row2, column);
413,6 → 470,7
}
 
private class TableModelHandler implements TableModelListener {
@Override
public void tableChanged(TableModelEvent e) {
// If we're not sorting by anything, just pass the event along.
if (!isSorting()) {
487,6 → 545,7
}
 
private class MouseHandler extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
JTableHeader h = (JTableHeader) e.getSource();
TableColumnModel columnModel = h.getColumnModel();
523,6 → 582,7
this.priority = priority;
}
 
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Color color = c == null ? Color.GRAY : c.getBackground();
// In a compound sort, make each succesive triangle 20%
556,10 → 616,12
g.translate(-x, -y);
}
 
@Override
public int getIconWidth() {
return size;
}
 
@Override
public int getIconHeight() {
return size;
}
572,11 → 634,12
this.tableCellRenderer = tableCellRenderer;
}
 
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (c instanceof JLabel) {
JLabel l = (JLabel) c;
l.setHorizontalTextPosition(JLabel.LEFT);
l.setHorizontalTextPosition(SwingConstants.LEFT);
int modelColumn = table.convertColumnIndexToModel(column);
l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize()));
}
584,13 → 647,27
}
}
 
private static class Directive {
private int column;
private int direction;
@Immutable
public static class Directive {
private final int column;
private final int direction;
 
public Directive(int column, int direction) {
this.column = column;
this.direction = direction;
}
 
public final int getColumn() {
return this.column;
}
 
public final int getDirection() {
return this.direction;
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + " " + this.getDirection() + " on column " + this.getColumn();
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/task/element/CompanyAccessSQLElement.java
New file
0,0 → 1,49
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.task.element;
 
import org.openconcerto.sql.element.ConfSQLElement;
 
import java.util.ArrayList;
import java.util.List;
 
public class CompanyAccessSQLElement extends ConfSQLElement {
 
public CompanyAccessSQLElement() {
super("ACCES_SOCIETE");
 
}
 
@Override
protected List<String> getListFields() {
final List<String> l = new ArrayList<String>();
l.add("ID_USER_COMMON");
l.add("ID_SOCIETE_COMMON");
return l;
}
 
@Override
protected List<String> getComboFields() {
final List<String> l = new ArrayList<String>();
l.add("ID_USER_COMMON");
l.add("ID_SOCIETE_COMMON");
return l;
}
 
@Override
protected String createCode() {
return "common.company-access";
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/task/element/TaskSQLElementBase.java
15,8 → 15,6
 
import org.openconcerto.sql.element.ConfSQLElement;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.task.TM;
import org.openconcerto.utils.i18n.I18nUtils;
 
/**
* @author Sylvain CUAZ
23,10 → 21,6
*/
public abstract class TaskSQLElementBase extends ConfSQLElement {
 
{
this.setL18nPackageName(I18nUtils.getPackageName(TM.class));
}
 
public TaskSQLElementBase(final String tableName) {
super(tableName);
}
/trunk/OpenConcerto/src/org/openconcerto/task/translation/SQLElementNames_pl.xml
New file
0,0 → 1,8
<translations>
<element refid="common.company-access">
<FIELD name="USER_COMMON" label="User" />
<FIELD name="ID_SOCIETE_COMMON" label="Ustawienia dostępu" />
</element>
<element refid="TACHE_COMMON" name="task" />
<element refid="TACHE_RIGHTS" name="right for tasks" namePlural="rights for tasks" />
</translations>
/trunk/OpenConcerto/src/org/openconcerto/task/translation/messages_es.properties
New file
0,0 → 1,20
summary=Summary :
todoBefore=Para hacer antes de {date, date, medium} en {date, time, short} por {user}
todoBefore.col=Hacer antes
ok=OK
cancel=Cancelar
taskToDo=Descripción de la tarea
assignedTo=Asignado a
created=Created
completed=Completado
deleteForbidden=Solo puedes eliminar tareas que creaste
assignedBy=Asignado por {user}\nEn {date, date, medium} por {date, time, short}
delete=Borrar
deleteSelectedTasks=Borrar {count, plural, =0 {selected tasks} one {the selected task} other {the # selected tasks}}
addTask=Nueva tarea
hideHistory= Ocultar historial
showDetails=Mostrar detalles
details=Detalles
markDone=Marca hecha
moveOneDay=Mover por un día
showTaskAssignedTo=Mostrar tarea asignada a ...
/trunk/OpenConcerto/src/org/openconcerto/task/translation/SQLElementNames_en.xml
1,4 → 1,8
<translations>
<element refid="common.company-access">
<FIELD name="USER_COMMON" label="User" />
<FIELD name="ID_SOCIETE_COMMON" label="Allowed company access" />
</element>
<element refid="TACHE_COMMON" name="task" />
<element refid="TACHE_RIGHTS" name="right for tasks" namePlural="rights for tasks" />
</translations>
/trunk/OpenConcerto/src/org/openconcerto/task/translation/SQLElementNames_es.xml
New file
0,0 → 1,8
<translations>
<element refid="common.company-access">
<FIELD name="USER_COMMON" label="Usario" />
<FIELD name="ID_SOCIETE_COMMON" label="Acceso a la sociedad" />
</element>
<element refid="TACHE_COMMON" name="tarea" />
<element refid="TACHE_RIGHTS" name="derechos para tareas" namePlural="derechos para tareas" />
</translations>
/trunk/OpenConcerto/src/org/openconcerto/task/translation/SQLElementNames_fr.xml
1,4 → 1,8
<translations>
<element refid="common.company-access">
<FIELD name="USER_COMMON" label="Utilisateur" />
<FIELD name="ID_SOCIETE_COMMON" label="Accés à la société" />
</element>
<element refid="TACHE_COMMON" nameClass="feminine" name="tâche" />
<element refid="TACHE_RIGHTS" nameClass="masculine" name="droit pour les tâches" namePlural="droits pour les tâches" />
</translations>
/trunk/OpenConcerto/src/org/openconcerto/task/TodoListModel.java
152,7 → 152,7
for (TodoListElement elt : rowsDeleted) {
int index = this.elements.indexOf(elt);
if (index >= 0) {
removeRow(index);
elements.remove(index);
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/task/config/ComptaBasePropsConfiguration.java
23,10 → 23,12
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFilter;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.users.CompanyAccessSQLElement;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.users.UserCommonSQLElement;
import org.openconcerto.sql.users.rights.RightSQLElement;
import org.openconcerto.sql.users.rights.UserRightSQLElement;
import org.openconcerto.task.TM;
import org.openconcerto.task.element.CompanyAccessSQLElement;
import org.openconcerto.task.element.TaskRightSQLElement;
import org.openconcerto.task.element.TaskSQLElement;
import org.openconcerto.utils.BaseDirs;
33,6 → 35,8
import org.openconcerto.utils.DesktopEnvironment;
import org.openconcerto.utils.LogUtils;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.i18n.Grammar_fr;
import org.openconcerto.utils.i18n.NounClass;
 
import java.io.File;
import java.io.FileNotFoundException;
39,8 → 43,10
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
 
public abstract class ComptaBasePropsConfiguration extends PropsConfiguration {
125,13 → 131,21
}
 
@Override
protected List<String> getMappings() {
final List<String> res = new ArrayList<>(super.getMappings());
final String pkg = "/" + TM.class.getPackage().getName().replace('.', '/');
res.add(pkg + "/translation/SQLElementNames");
return res;
}
 
@Override
protected SQLElementDirectory createDirectory() {
final SQLElementDirectory dir = super.createDirectory();
 
// TACHE_COMMON points to SOCIETE but we never display it we don't need the full element
dir.addSQLElement(new ConfSQLElement("SOCIETE_COMMON", "une société", "sociétés"));
dir.addSQLElement(new ConfSQLElement("EXERCICE_COMMON", "un exercice", "exercices"));
dir.addSQLElement(new ConfSQLElement("ADRESSE_COMMON", "une adresse", "adresses"));
dir.addSQLElement(new ConfSQLElement("SOCIETE_COMMON", Grammar_fr.getInstance().createPhrase(NounClass.FEMININE, "société")));
dir.addSQLElement(new ConfSQLElement("EXERCICE_COMMON", Grammar_fr.getInstance().createPhrase(NounClass.MASCULINE, "exercice")));
dir.addSQLElement(new ConfSQLElement("ADRESSE_COMMON", Grammar_fr.getInstance().createPhrase(NounClass.FEMININE, "adresse")));
 
dir.addSQLElement(new TaskRightSQLElement());
dir.addSQLElement(new TaskSQLElement());
138,8 → 152,8
 
dir.addSQLElement(new UserCommonSQLElement(getRoot(), false));
dir.addSQLElement(new CompanyAccessSQLElement());
dir.addSQLElement(UserRightSQLElement.class);
dir.addSQLElement(RightSQLElement.class);
dir.addSQLElement(new UserRightSQLElement(getRoot()));
dir.addSQLElement(new RightSQLElement(getRoot()));
 
return dir;
}
164,8 → 178,15
 
protected final void setRowSociete(int id) {
this.idSociete = id;
this.rowSociete = getSystemRoot().findTable("SOCIETE_COMMON").getValidRow(this.getSocieteID());
final SQLTable tableSociete = getSystemRoot().findTable("SOCIETE_COMMON");
final SQLRow row = tableSociete.getRow(id);
if (row == null) {
throw new IllegalArgumentException("no row for id " + id + " in " + tableSociete);
} else if (!row.isValid()) {
throw new IllegalArgumentException("invalid row : " + row);
}
this.rowSociete = row;
}
 
public final SQLBase getSQLBaseSociete() {
return this.getRootSociete().getBase();
/trunk/OpenConcerto/src/org/openconcerto/task/ui/UserRightPanelDetail.java
179,14 → 179,15
private void swapOnDoubleClick(final JList list, MouseEvent e, String field) {
if (e.getClickCount() == 2) {
int index = list.locationToIndex(e.getPoint());
if (index >= 0) {
ListModel dlm = list.getModel();
Object item = dlm.getElementAt(index);
list.ensureIndexIsVisible(index);
User toUser = (User) item;
swapState(selectedUser, toUser, field);
 
}
}
}
 
protected void swapState(User user, User toUser, String field) {
final SQLSelect sel = new SQLSelect();
/trunk/OpenConcerto/src/org/openconcerto/erp/panel/ITreeSelection.java
42,8 → 42,10
import java.beans.PropertyChangeSupport;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
 
import javax.swing.AbstractAction;
319,7 → 321,7
}
for (int i = 0; i < l.size(); i++) {
SQLRow row = l.get(i);
addNewNode(familles, row, row.getInt("ID_" + element.getTable().getName() + "_PERE"));
addNewNode(familles, row, row.getInt("ID_" + element.getTable().getName() + "_PERE"), new HashSet<>());
}
expandRow(0);
}
338,14 → 340,17
* @param id
* @param idPere
*/
private void addNewNode(Map<Integer, SQLRow> familles, SQLRow row, int idPere) {
private void addNewNode(Map<Integer, SQLRow> familles, SQLRow row, int idPere, Set<Integer> addedIDs) {
if (row != null && this.mapNode.get(row.getID()) == null) {
ITreeSelectionNode nodePere = this.mapNode.get(Integer.valueOf(idPere));
if (idPere > 1 && nodePere == null && familles != null) {
final SQLRow rowPere = familles.get(idPere);
if (rowPere.getID() != row.getID()) {
addNewNode(familles, rowPere, rowPere.getInt("ID_" + element.getTable().getName() + "_PERE"));
if (idPere != row.getID()) {
if (!addedIDs.contains(idPere)) {
addedIDs.add(idPere);
addNewNode(familles, rowPere, rowPere.getInt("ID_" + element.getTable().getName() + "_PERE"), addedIDs);
}
}
nodePere = this.mapNode.get(Integer.valueOf(idPere));
}
final ITreeSelectionNode newNode = new ITreeSelectionNode(row);
425,7 → 430,7
public void rowAdded(SQLTable table, int id) {
final SQLRow row = table.getRow(id);
int idPere = row.getInt("ID_" + element.getTable().getName() + "_PERE");
addNewNode(null, row, idPere);
addNewNode(null, row, idPere, new HashSet<>());
}
 
public void rowDeleted(SQLTable table, int id) {
/trunk/OpenConcerto/src/org/openconcerto/erp/panel/UserExitPanel.java
219,6 → 219,7
p.waitFor();
 
}
ComptaPropsConfiguration.getInstanceCompta().tearDownLogging(true);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
266,6 → 267,7
}
try {
Configuration.getInstance().destroy();
ComptaPropsConfiguration.getInstanceCompta().tearDownLogging(true);
} catch (Throwable e) {
e.printStackTrace();
}
/trunk/OpenConcerto/src/org/openconcerto/erp/panel/ChargementCreationSocietePanel.java
13,7 → 13,7
package org.openconcerto.erp.panel;
 
import org.openconcerto.erp.core.common.component.SocieteCommonSQLElement;
import org.openconcerto.erp.core.common.element.SocieteCommonSQLElement;
import org.openconcerto.erp.utils.ActionDB;
import org.openconcerto.erp.utils.StatusListener;
import org.openconcerto.sql.Configuration;
/trunk/OpenConcerto/src/org/openconcerto/erp/panel/PanelOOSQLComponent.java
18,6 → 18,7
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.preferences.GenerationDocGlobalPreferencePanel;
import org.openconcerto.erp.utils.TM;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.BaseSQLComponent;
import org.openconcerto.sql.element.SQLElement;
38,9 → 39,9
 
public class PanelOOSQLComponent extends JPanel {
 
private final JCheckBox checkImpression = new JCheckBox("Imprimer");
private final JCheckBox checkVisu = new JCheckBox("Visualiser");
private final JCheckBox checkAbo = new JCheckBox("Créer l'abonnement associé");
private final JCheckBox checkImpression = new JCheckBox(TM.tr("PanelOOSQLComponent.print")); //$NON-NLS-1$
private final JCheckBox checkVisu = new JCheckBox(TM.tr("PanelOOSQLComponent.view")); //$NON-NLS-1$
private final JCheckBox checkAbo = new JCheckBox(TM.tr("PanelOOSQLComponent.createSubscription")); //$NON-NLS-1$
 
public PanelOOSQLComponent(final BaseSQLComponent comp) {
super(new GridBagLayout());
49,7 → 50,7
this.setOpaque(false);
 
final SQLTable tableComp = comp.getElement().getTable();
if (tableComp.getName().equals("SAISIE_VENTE_FACTURE") && tableComp.getDBRoot().contains("ABONNEMENT")) {
if (tableComp.getName().equals("SAISIE_VENTE_FACTURE") && tableComp.getDBRoot().contains("ABONNEMENT")) { //$NON-NLS-1$ //$NON-NLS-2$
this.checkAbo.setOpaque(false);
this.add(this.checkAbo, c);
}
57,22 → 58,22
SQLPreferences prefs = SQLPreferences.getMemCached(((ComptaPropsConfiguration) Configuration.getInstance()).getRootSociete());
if (prefs.getBoolean(GenerationDocGlobalPreferencePanel.MULTIMOD, false)) {
 
if (tableComp.getFieldsName().contains("ID_MODELE")) {
String labelFor = comp.getLabelFor("ID_MODELE");
if (tableComp.getFieldsName().contains("ID_MODELE")) { //$NON-NLS-1$
String labelFor = comp.getLabelFor("ID_MODELE"); //$NON-NLS-1$
if (labelFor == null || labelFor.trim().length() == 0) {
labelFor = "Modéles";
labelFor = "Modéles"; //$NON-NLS-1$
}
JLabel labelModele = new JLabel(labelFor);
ElementComboBox boxModele = new ElementComboBox(true, 25);
SQLElement modeleElement = Configuration.getInstance().getDirectory().getElement("MODELE");
SQLElement modeleElement = Configuration.getInstance().getDirectory().getElement("MODELE"); //$NON-NLS-1$
boxModele.init(modeleElement, modeleElement.getComboRequest(true));
comp.addView(boxModele, "ID_MODELE");
comp.addView(boxModele, "ID_MODELE"); //$NON-NLS-1$
boxModele.getRequest().setSelectTransf(new ITransformer<SQLSelect, SQLSelect>() {
 
@Override
public SQLSelect transformChecked(SQLSelect input) {
SQLTable table = Configuration.getInstance().getDirectory().getElement("TYPE_MODELE").getTable();
Where w = new Where(input.getAlias(table.getField("TABLE")), "=", tableComp.getName());
SQLTable table = Configuration.getInstance().getDirectory().getElement("TYPE_MODELE").getTable(); //$NON-NLS-1$
Where w = new Where(input.getAlias(table.getField("TABLE")), "=", tableComp.getName()); //$NON-NLS-1$ //$NON-NLS-2$
input.setWhere(w);
return input;
}
81,7 → 82,7
DefaultGridBagConstraints.lockMinimumSize(boxModele);
this.add(boxModele, c);
} else {
System.err.println("Impossible d'ajouter la combo pour le choix des modèles car le champ ID_MODELE n'est pas présent dans la table " + tableComp.getName());
System.err.println(TM.tr("PanelOOSQLComponent.missingField", tableComp.getName())); //$NON-NLS-1$
Thread.dumpStack();
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/panel/compta/ExportFEC.java
84,7 → 84,9
 
sel.addFieldOrder(tableEcriture.getField("DATE"));
sel.addFieldOrder(tableMouvement.getField("NUMERO"));
sel.setWhere(sel.getWhere().and(new Where(tableEcriture.getField("DEBIT"), "!=", tableEcriture.getField("CREDIT"))));
final Where w = new Where(tableEcriture.getField("DEBIT"), "!=", tableEcriture.getField("CREDIT"));
final Where w2 = new Where(tableEcriture.getField("CLOTURE"), "!=", Boolean.TRUE);
sel.setWhere(sel.getWhere().and(w).and(w2));
 
@SuppressWarnings("unchecked")
final List<Object[]> l = (List<Object[]>) this.getRootSociete().getDBSystemRoot().getDataSource().execute(sel.asString(), new ArrayListHandler());
105,8 → 107,8
if (s == null) {
throw new NullPointerException("Valeur manquante pour remplir la ligne : " + line);
}
// TODO remove \r
line.add(s.trim().replace(ZONE_SEPARATOR, REPLACEMENT).replace(RECORD_SEPARATOR, REPLACEMENT));
final String escapedString = StringUtils.toAsciiString(s).trim();
line.add(escapedString.replace(ZONE_SEPARATOR, REPLACEMENT).replace(RECORD_SEPARATOR, REPLACEMENT));
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/erp/model/MouseSheetXmlListeListener.java
26,6 → 26,7
import org.openconcerto.sql.view.list.RowAction;
import org.openconcerto.ui.EmailComposer;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.ListMap;
 
import java.awt.event.ActionEvent;
import java.io.File;
33,6 → 34,7
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
 
import javax.swing.AbstractAction;
import javax.swing.Action;
111,15 → 113,18
}
 
protected void sendMail(final AbstractSheetXml sheet, final boolean readOnly) {
List<AbstractSheetXml> l = new ArrayList<AbstractSheetXml>(1);
List<AbstractSheetXml> l = new ArrayList<>(1);
l.add(sheet);
sendMail(l, readOnly);
}
 
protected void sendMail(final List<AbstractSheetXml> sheets, final boolean readOnly) {
final Thread t = new Thread() {
@Override
public void run() {
ListMap<String, File> mailFilesMap = new ListMap<>();
for (AbstractSheetXml sheet : sheets) {
String mail = "";
 
for (AbstractSheetXml sheet : sheets) {
final SQLRow row = sheet.getSQLRow();
Set<SQLField> setContact = null;
SQLTable tableContact = Configuration.getInstance().getRoot().findTable("CONTACT");
134,7 → 139,7
mail = row.getForeignRow(field.getName()).getString("EMAIL");
}
}
 
String nomClient = "";
if (setClient != null && (mail == null || mail.trim().length() == 0)) {
for (SQLField field : setClient) {
SQLRow rowCli = row.getForeignRow(field.getName());
141,9 → 146,18
if (mail == null || mail.trim().length() == 0) {
mail = rowCli.getString("MAIL");
}
nomClient = rowCli.getString("NOM");
}
}
 
if (mail.trim().length() == 0) {
mail = nomClient;
}
 
String table = row.getTable().getName();
if (table.equalsIgnoreCase("COMMANDE") || table.equalsIgnoreCase("DEMANDE_PRIX") || table.equalsIgnoreCase("FACTURE_FOURNISSEUR")) {
mail = "";
}
if (mail == null || mail.trim().length() == 0) {
SQLTable tableF = ((ComptaPropsConfiguration) Configuration.getInstance()).getSQLBaseSociete().getTable("FOURNISSEUR");
Set<SQLField> setF = null;
195,20 → 209,21
}
}
}
try {
if (readOnly) {
mailFilesMap.add(mail, sheet.getOrCreatePDFDocumentFile(true).getAbsoluteFile());
} else {
mailFilesMap.add(mail, sheet.getOrCreateDocumentFile().getAbsoluteFile());
 
}
final String adresseMail = mail;
} catch (Exception e) {
ExceptionHandler.handle("Impossible de charger le document PDF", e);
}
}
 
final String subject = sheets.get(0).getReference();
for (final String mailDest : mailFilesMap.keySet()) {
 
if (readOnly) {
final Thread t = new Thread() {
@Override
public void run() {
final List<File> files = new ArrayList<File>();
try {
for (AbstractSheetXml sheet : sheets) {
files.add(sheet.getOrCreatePDFDocumentFile(true).getAbsoluteFile());
}
final List<File> files = mailFilesMap.get(mailDest);
 
SwingUtilities.invokeLater(new Runnable() {
 
215,31 → 230,27
@Override
public void run() {
try {
EmailComposer.getInstance().compose(adresseMail, subject + (subject.trim().length() == 0 ? "" : ", ") + files.get(0).getName(),
getMailObject(sheets.get(0).getSQLRow()), files.toArray(new File[files.size()]));
String subject = sheets.get(0).getReference();
if (subject.isEmpty()) {
final StringJoiner joiner = new StringJoiner(", ");
for (File f : files) {
joiner.add(f.getName());
}
subject = joiner.toString();
}
final String message = getMailObject(sheets.get(0).getSQLRow());
EmailComposer.getInstance().compose(mailDest, subject, message, files.toArray(new File[files.size()]));
} catch (Exception e) {
ExceptionHandler.handle("Impossible de charger le document PDF dans l'email!", e);
ExceptionHandler.handle("Impossible d'envoyer le courriel!", e);
}
}
});
} catch (Exception e) {
ExceptionHandler.handle("Impossible de charger le document PDF", e);
}
 
}
 
};
t.start();
} else {
try {
final List<File> files = new ArrayList<File>();
for (AbstractSheetXml sheet : sheets) {
files.add(sheet.getGeneratedFile().getAbsoluteFile());
}
EmailComposer.getInstance().compose(adresseMail, subject + (subject.trim().length() == 0 ? "" : ", ") + sheets.get(0).getGeneratedFile().getName(),
getMailObject(sheets.get(0).getSQLRow()), files.toArray(new File[files.size()]));
} catch (Exception exn) {
ExceptionHandler.handle(null, "Impossible de créer le courriel", exn);
}
}
 
}
 
365,7 → 376,7
 
@Override
public boolean enabledFor(List<SQLRowValues> selection) {
return selection != null && selection.size() > 0;
return selection != null && !selection.isEmpty();
}
 
});
/trunk/OpenConcerto/src/org/openconcerto/erp/model/FichePayeModel.java
72,7 → 72,7
private SQLJavaEditor javaEdit = new SQLJavaEditor(VariablePayeSQLElement.getMapTree());
 
// liste des variable de paye à calculer
private BigDecimal salBrut, salBrutBase, salBrutCotis, salBrutTaxable, cotPat, cotSal, taxCmPat, taxCmSal, netImp, netAPayer, csg, csgSansAbattement, cice, allegmentCotisation, avantage,
private BigDecimal salBrut, salBrutBase, salBrutCotis, salBrutTaxable, cotPat, cotSal, taxCmPat, taxCmSal, netImp, pas, netAPayer, csg, csgSansAbattement, cice, allegmentCotisation, avantage,
reduction;
 
private Map<Integer, String> mapField;
154,14 → 154,22
return csgSansAbattement;
}
 
public BigDecimal getPas() {
return pas;
}
 
public BigDecimal getNetAPayerTotal() {
return netAPayer.add(this.salBrut);
}
 
public BigDecimal getNetImpTotal() {
return netImp.add(this.salBrut);
return netImp.add(this.salBrutTaxable);
}
 
public BigDecimal getNetAvantPas() {
return getNetAPayerTotal().subtract(this.pas);
}
 
public BigDecimal getSalBrut() {
return salBrut;
}
206,6 → 214,7
this.taxCmPat = BigDecimal.ZERO;
this.taxCmSal = BigDecimal.ZERO;
this.netAPayer = BigDecimal.ZERO;
this.pas = BigDecimal.ZERO;
this.netImp = BigDecimal.ZERO;
this.csg = BigDecimal.ZERO;
this.csgSansAbattement = BigDecimal.ZERO;
757,6 → 766,7
rowValsFiche.put("SAL_BRUT_TAXABLE", this.salBrutTaxable);
rowValsFiche.put("NET_IMP", getNetImpTotal());
rowValsFiche.put("NET_A_PAYER", getNetAPayerTotal());
rowValsFiche.put("NET_AVANT_PAS", getNetAvantPas());
rowValsFiche.put("COT_SAL", this.cotSal);
rowValsFiche.put("COT_PAT", this.cotPat);
rowValsFiche.put("TAXE_CM_PAT", this.taxCmPat);
763,6 → 773,8
rowValsFiche.put("TAXE_CM_SAL", this.taxCmSal);
rowValsFiche.put("CSG", getCsgTotal());
rowValsFiche.put("HEURE_TRAV", getHeureTrav());
rowValsFiche.put("TOTAL_PAS", getPas());
 
if (this.cice == null) {
if (salBrut.signum() > 0
&& salBrut.compareTo(new BigDecimal(getSmicHoraire(tableFichePaye.getDBRoot())).multiply(getHeureTrav()).multiply(new BigDecimal(2.5), DecimalUtils.HIGH_PRECISION)) <= 0) {
954,6 → 966,9
if (rowSource.getBoolean("IMPOSABLE")) {
this.netImp = this.netImp.subtract(montant);
}
if (rowSource.getBoolean("PAS")) {
this.pas = this.pas.subtract(montant);
}
 
} // Gain
else {
964,7 → 979,10
if (rowSource.getBoolean("IMPOSABLE")) {
this.netImp = this.netImp.add(montant);
}
if (rowSource.getBoolean("PAS")) {
this.pas = this.pas.add(montant);
}
}
 
// Mis a jour du salaire net
// updateValueFiche();
975,7 → 993,10
if (rowSource.getBoolean("IMPOSABLE")) {
this.netImp = this.netImp.subtract(ded);
}
if (rowSource.getBoolean("PAS")) {
this.pas = this.pas.subtract(ded);
}
}
BigDecimal add = rowVals.getBigDecimal("MONTANT_SAL_AJ");
if (add != null) {
this.netAPayer = this.netAPayer.add(add);
982,10 → 1003,13
if (rowSource.getBoolean("IMPOSABLE")) {
this.netImp = this.netImp.add(add);
}
if (rowSource.getBoolean("IMPOSABLE")) {
this.pas = this.pas.add(add);
}
}
}
}
}
 
private void loadElementNet(SQLRow rowSource, SQLRow row) {
SQLRowValues rowVals = new SQLRowValues(tableFichePayeElt);
1057,7 → 1081,12
 
// CICE
int codeTP = rowSource.getForeignID("ID_CODE_CAISSE_TYPE_RUBRIQUE");
if (codeTP == 298) {
String codeStringTP = "";
if (rowSource.getForeign("ID_CODE_CAISSE_TYPE_RUBRIQUE") != null && !rowSource.isForeignEmpty("ID_CODE_CAISSE_TYPE_RUBRIQUE")) {
codeStringTP = rowSource.getForeign("ID_CODE_CAISSE_TYPE_RUBRIQUE").getString("CODE");
}
 
if (codeTP == 298 || codeStringTP.equals("400")) {
this.cice = rowVals.getBigDecimal("NB_BASE");
}
 
1064,6 → 1093,7
// Mis a jour des cotisations
// updateValueFiche();
}
 
}
 
private void loadElementCotisation(final SQLRow rowSource, SQLRow row) {
1127,6 → 1157,7
return;
}
 
try {
Object baseOb = this.javaEdit.checkFormule(rowSource.getString("NB_BASE"), "BASE");
Object tauxSalOb = this.javaEdit.checkFormule(rowSource.getString("TAUX_SAL"), "SAL");
Object tauxPatOb = this.javaEdit.checkFormule(rowSource.getString("TAUX_PAT"), "PAT");
1144,7 → 1175,6
rowVals.put("MONTANT_SAL_DED", montantDed);
 
boolean b = isEltImprimable(rowSource, rowVals);
// System.err.println("Impression --- > " + b);
rowVals.put("IMPRESSION", Boolean.valueOf(b));
 
if (rowSource.getBoolean("REDUCTION_GVT_COM")) {
1157,7 → 1187,10
}
 
this.vectRubrique.add(rowVals);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, "Une des formules de la rubrique de commentaire " + rowSource.getString("CODE") + " -- " + rowSource.getString("NOM") + " n'est pas correcte!");
}
}
 
private void calculValue() {
 
1165,9 → 1198,6
 
resetValueFiche();
 
/*
* this.threadUpdate = new Thread("Update Fiche Paye") { public void run() {
*/
Vector<SQLRowValues> vectTmp = new Vector<SQLRowValues>(this.vectRubrique);
 
this.vectRubrique = new Vector<SQLRowValues>();
1180,8 → 1210,6
 
if (source.trim().length() != 0) {
 
// System.err.println("Source != null");
 
if (this.mapTableSource.get(source) != null) {
SQLRow rowSource = this.mapTableSource.get(source).getRow(idSource);
 
1205,9 → 1233,6
System.err.println(this.vectRubrique.size() + " elements ADDed ");
updateValueFiche();
fireTableDataChanged();
/*
* } }; this.threadUpdate.start();
*/
 
System.err.println("End calculValue At " + new Date());
}
/trunk/OpenConcerto/src/org/openconcerto/erp/rights/UserRightGroupComptaSQLElement.java
13,9 → 13,9
package org.openconcerto.erp.rights;
 
import org.openconcerto.sql.element.GlobalMapper;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.users.rights.UserRightSQLComponent;
import org.openconcerto.sql.users.rights.UserRightSQLElement;
 
23,8 → 23,8
 
public class UserRightGroupComptaSQLElement extends UserRightSQLElement {
 
public UserRightGroupComptaSQLElement() {
super();
public UserRightGroupComptaSQLElement(final DBRoot r) {
super(r);
// ((Group)GlobalMapper.getInstance().get(UserRightSQLComponent.ID)).
}
 
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/translation/messages_pl.properties
7,4 → 7,7
apply.associated.pricelist.to.customer=Zastosuj cennik klienta
invoice.address.same.main.address=Adres faktury same jak g\u0142ówny adres
delivery.address.same.main.address=Adres dostawy same jak g\u0142ówny adres
additional.address=Dodatkowy adres
additional.address=Dodatkowy adres
 
createMenuItem.name={elem__singular}
listMenuItem.name=List of {elem__plural}
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/translation/messages_en.properties
12,6 → 12,8
delivery.address.same.main.address=Delivery address same as main address
additional.address=Additional address
 
register.missing.title=Configuration error
register.missing=The register n° {0} doesn\u2019t exist in the database.\n\nPlease create it or modify this host's configuration.
register.moved.title=Different host
register.moved=<html>It seems that this register has been moved.\
You can ignore this message if for instance :\
24,7 → 26,36
register.moved.ignore=Ignore
register.moved.quit=Quit
 
register.notReconciled.title=Incoherence of the opening state of the register
register.notReconciled.outsideMeddling=The files and/or data base were modified outside of this software.
register.notReconciled.resumeFailed=The opening or closure was interrupted, the process was resumed but it failed.
register.notReconciled.unknown=The opening of the register on this host and in the database was incoherent. \
The state couldn\u2019t be reconciled.
# local open
register.notReconciled.open.datesMismatch=The register was opened at different dates on this host ({localDate,date} at {localDate,time}) \
and in the database ({remoteDate,date} at {remoteDate,time}).
register.notReconciled.localOpen_remoteClosed=The register is opened on this host ({localDate,date} at {localDate,time}) \
but closed in the database ({remoteDate,date} at {remoteDate,time}).
register.notReconciled.localOpen_remoteMissing=The register is opened on this host ({localDate,date} at {localDate,time}) \
but was never opened in the database.
# remote open
register.notReconciled.localMissing_remoteReopen=The register was opened multiple times in the database \
(the last time {remoteDate,date} at {remoteDate,time}), but never on this host.
register.notReconciled.localOpenFailed_remoteOpen=The register was open in the database ({remoteDate,date} at {remoteDate,time}) \
and the opening on this host failed.
register.notReconciled.localClosed_remoteCloseFailed=The register was closed on this host ({localDate,date} at {localDate,time}) \
and the closure in the database failed.
register.notReconciled.localClosed_remoteOpen.datesMismatch=The register is closed on this host ({localDate,date} at {localDate,time}) \
but open in the database ({remoteDate,date} at {remoteDate,time}). The opening and closure dates don\u2019t match.
# both closed
register.notReconciled.localMissing_remoteClosed=The register is closed in the database ({remoteDate,date} at {remoteDate,time}) \
but was never used on this host.
register.notReconciled.closed.datesMismatch=The register is closed on this host ({localDate,date} at {localDate,time}) \
and in the database ({remoteDate,date} at {remoteDate,time}), but with different dates.
register.notReconciled.localClosed_remoteMissing=The register is closed on this host ({localDate,date} at {localDate,time}) \
but the database is empty.
 
 
sddMessage.generation.noneNeeded=No invoice needs {msgElem__singularIndefiniteArticle}.
sddMessage.generation.noneIgnored=The generated {msgElem__singular} includes {invoiceElem__definiteNumeral}.
sddMessage.generation.someIgnored={invoiceElemCount, plural, =0 {All invoices needing {msgElem__singularIndefiniteArticle} were ignored :}\
33,3 → 64,47
sddMessage.generation.someIgnored.duplicateMandate={duplicateCount, plural, =1 {one because its mandate was shared with another invoice}\
other {# because their mandates were shared with other invoices}}. You must generate again {msgElem__singularIndefiniteArticle}.
sddMessage.generation.someIgnored.missingInfo={missingInfoCount, plural, =1 {one because some information was missing} other {# because some information was missing}}
 
sales.shipment.allShipments=All shipments
sales.shipment.nonInvoicedShipments=Non invoiced shipments
sales.shipment.invoicedShipments=Invoiced shipments
sales.shipment.listTotal=Total of the shipments in the list
 
dateFilter.none=No filter
dateFilter.currentYear=Current year
dateFilter.previousYear=Previous year
dateFilter.currentMonth=Current month
dateFilter.previousMonth=Previous month
dateFilter.currentWeek=Current week
dateFilter.previousWeek=Previous week
dateFilter.currentFiscalYear=Current fiscal year
dateFilter.custom=Custom
dateFilter.range=Range
dateFilter.from=From
dateFilter.to=To
 
TotalPanel.global=Global
TotalPanel.margin=Margin
TotalPanel.purchaseTotal.taxExcluded=Purchase total excl tax
TotalPanel.service.taxExcluded=Service included excl tax
TotalPanel.total.taxExcluded=Total excl tax
TotalPanel.total.taxIncluded=Total incl tax
TotalPanel.total.VAT=VAT total
TotalPanel.currencyTotal=Currency total
TotalPanel.ecoTax=Dont Eco-Contrib.
TotalPanel.estimatedBudget=Estimated budget
TotalPanel.selection=Selection
 
deliveryForm.applyClientRates=Apply client rates?
deliveryForm.cannotAdd.existingNumber=Cannot add, form number already exists.
deliveryForm.cannotCreate.notInStock=Cannot create the form, the following articles are out of stock :
deliveryForm.numberRefreshed=The number has been refreshed to {0}
deliveryForm.shipAll=Ship all
 
createMenuItem.name={elem__singular}
listMenuItem.name=List of {elem__plural}
 
PanelOOSQLComponent.createSubscription=Create subscription
PanelOOSQLComponent.missingField=Cannot add a combo to choose templates because the field ID_MODELE is missing in the table {0}
PanelOOSQLComponent.print=Print
PanelOOSQLComponent.view=View
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/translation/messages_es.properties
New file
0,0 → 1,78
add=Ajouter
modify.or.delete=Modifier/Supprimer
address=Adresse
address.type.invoice=Facturation
address.type.delivery=Livraison
address.type.external=Mandataire
address.type.other=Autre
product.bom.expand.warning=Attention en éclatant la nomenclature le prix total sera calculé à partir du prix des composants!
product.bom.flatexpand.warning=Attention en applatissant la nomenclature le kit sera remplacé par ses composants!
apply.associated.pricelist.to.customer=Appliquer les tarifs associés au client
invoice.address.same.main.address=Adresse de facturation identique à la principale
delivery.address.same.main.address=Adresse de livraison identique à l\u2019adresse principale
additional.address=Adresses supplémentaires
 
register.moved.title=Hôte différent
register.moved=<html>Il semble que cette caisse ait été déplacée.\
Vous pouvez ignorer ce message si par exemple :\
<ul>\
<li>la session utilisateur a été renommé,</li>\
<li>l\u2019ordinateur a été renommé,</li>\
<li>la caisse a été déplacé vers un nouvel ordinateur.</li>\
</ul><p>Si ce n\u2019est pas le cas, merci de vérifier qu\u2019il n\u2019y ait pas plusieurs installations configurées avec le même numéro de caisse (n° {0}).\
<p>Voulez-vous ignorer message et continuer ou alors quitter l\u2019application ?</html>
register.moved.ignore=Ignorer
register.moved.quit=Quitter
 
sddMessage.generation.noneNeeded=Aucune facture ne nécessite {msgElem__de__singular}.
sddMessage.generation.noneIgnored={msgElem__singularDefiniteArticle} généré inclut {invoiceElem__definiteNumeral}.
sddMessage.generation.someIgnored={invoiceElemCount, plural, =0 {Toutes les factures nécessitant {msgElem__singularIndefiniteArticle} ont été ignorées :}\
other {{msgElem__singularDefiniteArticle} généré inclut {invoiceElem__indefiniteNumeral}, d\u2019autres ont été ignorées :}}
sddMessage.generation.someIgnored.future={futureCount, plural, =1 {une car sa date d\u2019échéance est trop éloignée} other {# car leurs dates d\u2019échéance sont trop éloignées}}
sddMessage.generation.someIgnored.duplicateMandate={duplicateCount, plural, =1 {une car son mandat est partagé avec une autre facture}\
other {# car leurs mandats sont partagés avec d\u2019autres factures}}. Vous devez générer à nouveau {msgElem__singularIndefiniteArticle}.
sddMessage.generation.someIgnored.missingInfo={missingInfoCount, plural, =1 {une car il manquait des informations} other {# car il manquait des informations}}
 
sales.shipment.allShipments=Todas las salidas
sales.shipment.nonInvoicedShipments=Salidas no facturadas
sales.shipment.invoicedShipments=Salidas facturas
sales.shipment.listTotal=Total salidas de la lista
 
dateFilter.none=Sin filtrar
dateFilter.currentYear=Año corriente
dateFilter.previousYear=Año anterior
dateFilter.currentMonth=Mes corriente
dateFilter.previousMonth=Mes anterior
dateFilter.currentWeek=Semana corriente
dateFilter.previousWeek=Semana anterior
dateFilter.currentFiscalYear=Ejercicio corriente
dateFilter.custom=Personalizado
dateFilter.range=Periodo
dateFilter.from=Del
dateFilter.to=Al
 
TotalPanel.global=Global
TotalPanel.margin=Margen
TotalPanel.purchaseTotal.taxExcluded=Total compra bruto
TotalPanel.service.taxExcluded=Service HT inclus
TotalPanel.total.taxExcluded=Total bruto
TotalPanel.total.taxIncluded=Total TTC
TotalPanel.total.VAT=Total TVA
TotalPanel.currencyTotal=Total Devise
TotalPanel.ecoTax=Dont Eco-Contrib.
TotalPanel.estimatedBudget=Budget prévisionnel
TotalPanel.selection=Selección
 
deliveryForm.applyClientRates=Appliquer les tarifs associés au client?
deliveryForm.cannotAdd.existingNumber=Impossible d\u2019ajouter, numéro de bon existant.
deliveryForm.cannotCreate.notInStock=Impossible de créer le BL, Les articles suivants ne sont pas en stock :
deliveryForm.numberRefreshed=Le numéro a été actualisé en {0}
deliveryForm.shipAll=Entregar todo
 
createMenuItem.name={elem__singular}
listMenuItem.name=Lista de {elem__plural}
 
PanelOOSQLComponent.createSubscription=Create subscription
PanelOOSQLComponent.missingField=Cannot add a combo to choose templates because the field ID_MODELE is missing in the table {0}
PanelOOSQLComponent.print=Imprimir
PanelOOSQLComponent.view=Visualizar
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/translation/messages_fr.properties
9,9 → 9,11
product.bom.flatexpand.warning=Attention en applatissant la nomenclature le kit sera remplacé par ses composants!
apply.associated.pricelist.to.customer=Appliquer les tarifs associés au client
invoice.address.same.main.address=Adresse de facturation identique à la principale
delivery.address.same.main.address=Adresse de livraison identique à l'adresse principale
delivery.address.same.main.address=Adresse de livraison identique à l\u2019adresse principale
additional.address=Adresses supplémentaires
 
register.missing.title=Erreur de configuration
register.missing=La caisse n° {0} n\u2019existe pas dans la base de données.\n\nMerci de la créer ou de corriger le paramétrage de ce poste.
register.moved.title=Hôte différent
register.moved=<html>Il semble que cette caisse ait été déplacée.\
Vous pouvez ignorer ce message si par exemple :\
19,16 → 21,94
<li>la session utilisateur a été renommé,</li>\
<li>l\u2019ordinateur a été renommé,</li>\
<li>la caisse a été déplacé vers un nouvel ordinateur.</li>\
</ul><p>Si ce n'est pas le cas, merci de vérifier qu'il n'y ait pas plusieurs installations configurées avec le même numéro de caisse (n° {0}).\
</ul><p>Si ce n\u2019est pas le cas, merci de vérifier qu\u2019il n\u2019y ait pas plusieurs installations configurées avec le même numéro de caisse (n° {0}).\
<p>Voulez-vous ignorer message et continuer ou alors quitter l\u2019application ?</html>
register.moved.ignore=Ignorer
register.moved.quit=Quitter
 
register.notReconciled.title=Incohérence d\u2019ouverture de la caisse
register.notReconciled.outsideMeddling=Les fichiers et/ou la base de données ont été modifiés en dehors de ce logiciel.
register.notReconciled.resumeFailed=L\u2019ouverture ou la clôture avait été interrompue, elle a repris sans succès.
register.notReconciled.unknown=L\u2019ouverture de la caisse sur ce poste et dans la base de donnée était incohérente. \
L\u2019état n\u2019a pas pu être réconcilié.
# local open
register.notReconciled.open.datesMismatch=La caisse est ouverte à des dates différentes sur ce poste (le {localDate,date} à {localDate,time}) \
et dans la base de donnée (le {remoteDate,date} à {remoteDate,time}).
register.notReconciled.localOpen_remoteClosed=La caisse est ouverte sur ce poste (le {localDate,date} à {localDate,time}) \
mais fermée dans la base de donnée (le {remoteDate,date} à {remoteDate,time}).
register.notReconciled.localOpen_remoteMissing=La caisse est ouverte sur ce poste (le {localDate,date} à {localDate,time}) \
mais n\u2019a jamais été ouverte dans la base de donnée.
# remote open
register.notReconciled.localMissing_remoteReopen=La caisse a été ouverte plusieurs fois dans la base de donnée \
(la dernière fois le {remoteDate,date} à {remoteDate,time}), mais jamais sur ce poste.
register.notReconciled.localOpenFailed_remoteOpen=La caisse avait été ouverte dans la base de donnée (le {remoteDate,date} à {remoteDate,time}) \
et l\u2019ouverture sur ce poste a échouée.
register.notReconciled.localClosed_remoteCloseFailed=La caisse avait été fermée sur ce poste (le {localDate,date} à {localDate,time}) \
et la clôture dans la base de donnée a échouée.
register.notReconciled.localClosed_remoteOpen.datesMismatch=La caisse est fermée sur ce poste (le {localDate,date} à {localDate,time}) \
mais ouverte dans la base de donnée (le {remoteDate,date} à {remoteDate,time}). Les dates d\u2019ouverture et de clôture précédentes ne concordent pas.
# both closed
register.notReconciled.localMissing_remoteClosed=La caisse est fermée dans la base de donnée (le {remoteDate,date} à {remoteDate,time}), \
mais n\u2019a jamais été utilisée sur ce poste.
register.notReconciled.closed.datesMismatch=La caisse est fermée sur ce poste (le {localDate,date} à {localDate,time}) \
et dans la base de donnée (le {remoteDate,date} à {remoteDate,time}), mais les dates ne concordent pas.
register.notReconciled.localClosed_remoteMissing=La caisse est fermée sur ce poste (le {localDate,date} à {localDate,time}) \
mais la base de donnée est vide.
 
 
sddMessage.generation.noneNeeded=Aucune facture ne nécessite {msgElem__de__singular}.
sddMessage.generation.noneIgnored={msgElem__singularDefiniteArticle} généré inclut {invoiceElem__definiteNumeral}.
sddMessage.generation.someIgnored={invoiceElemCount, plural, =0 {Toutes les factures nécessitant {msgElem__singularIndefiniteArticle} ont été ignorées :}\
other {{msgElem__singularDefiniteArticle} généré inclut {invoiceElem__indefiniteNumeral}, d'autres ont été ignorées :}}
other {{msgElem__singularDefiniteArticle} généré inclut {invoiceElem__indefiniteNumeral}, d\u2019autres ont été ignorées :}}
sddMessage.generation.someIgnored.future={futureCount, plural, =1 {une car sa date d\u2019échéance est trop éloignée} other {# car leurs dates d\u2019échéance sont trop éloignées}}
sddMessage.generation.someIgnored.duplicateMandate={duplicateCount, plural, =1 {une car son mandat est partagé avec une autre facture}\
other {# car leurs mandats sont partagés avec d\u2019autres factures}}. Vous devez générer à nouveau {msgElem__singularIndefiniteArticle}.
sddMessage.generation.someIgnored.missingInfo={missingInfoCount, plural, =1 {une car il manquait des informations} other {# car il manquait des informations}}
 
sales.shipment.allShipments=Toutes les livraisons
sales.shipment.nonInvoicedShipments=Livraisons non facturées
sales.shipment.invoicedShipments=Livraisons facturées
sales.shipment.listTotal=Total des livraisons de la liste
 
dateFilter.none=Sans filtrage
dateFilter.currentYear=Année courante
dateFilter.previousYear=Année précédente
dateFilter.currentMonth=Mois courant
dateFilter.previousMonth=Mois précédent
dateFilter.currentWeek=Semaine courante
dateFilter.previousWeek=Semaine précédente
dateFilter.currentFiscalYear=Exercice courant
dateFilter.custom=Personnalisée
dateFilter.range=Période
dateFilter.from=Du
dateFilter.to=Au
 
TotalPanel.global=Global
TotalPanel.margin=Marge
TotalPanel.purchaseTotal.taxExcluded=Total achat HT
TotalPanel.service.taxExcluded=Service HT inclus
TotalPanel.total.taxExcluded=Total HT
TotalPanel.total.taxIncluded=Total TTC
TotalPanel.total.VAT=Total TVA
TotalPanel.currencyTotal=Total Devise
TotalPanel.ecoTax=Dont Eco-Contrib.
TotalPanel.estimatedBudget=Budget prévisionnel
TotalPanel.selection=Sélection
 
deliveryForm.applyClientRates=Appliquer les tarifs associés au client?
deliveryForm.cannotAdd.existingNumber=Impossible d\u2019ajouter, numéro de bon existant.
deliveryForm.cannotCreate.notInStock=Impossible de créer le BL, Les articles suivants ne sont pas en stock :
deliveryForm.numberRefreshed=Le numéro a été actualisé en {0}
deliveryForm.shipAll=Tout livrer
 
createMenuItem.name={elem__singular}
listMenuItem.name=Liste {elem__pluralIndefiniteArticle}
 
PanelOOSQLComponent.createSubscription=Créer l\u2019abonnement associé
PanelOOSQLComponent.missingField=Impossible d\u2019ajouter la combo pour le choix des modèles car le champ ID_MODELE n\u2019est pas présent dans la table {0}
PanelOOSQLComponent.print=Imprimer
PanelOOSQLComponent.view=Visualiser
 
 
accounting.editing.piece.label=Nom de la pièce comptable
 
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/ConvertDevise.java
23,20 → 23,15
/**
* Convertit un prix ht en ttc
*
* @param ht
* la valeur hors taxe à convertir
* @param taxe
* taux de la tva à appliquer (ex : 7, 20)
* @param scale
* précision à appliquer sur le ttc retourné
* @param ht la valeur hors taxe à convertir
* @param taxe taux de la tva à appliquer (ex : 7, 20)
* @param scale précision à appliquer sur le ttc retourné
* @return le ttc avec la précision scale
*/
public static final BigDecimal getTtcFromHt(BigDecimal ht, BigDecimal taxe,
int scale) {
public static final BigDecimal getTtcFromHt(BigDecimal ht, BigDecimal taxe, int scale) {
 
BigDecimal tauxB = taxe.movePointLeft(2).add(BigDecimal.ONE);
BigDecimal result = ht.multiply(tauxB, DecimalUtils.HIGH_PRECISION)
.setScale(scale, RoundingMode.HALF_UP);
BigDecimal result = ht.multiply(tauxB, DecimalUtils.HIGH_PRECISION).setScale(scale, RoundingMode.HALF_UP);
return result;
 
}
44,23 → 39,18
/**
* Convertit un prix ttc en ht
*
* @param tts
* la valeur tts à convertir
* @param taxe
* taux de la tva à appliquer (ex : 7, 20)
* @param scale
* précision à appliquer sur le ht retourné
* @param tts la valeur tts à convertir
* @param taxe taux de la tva à appliquer (ex : 7, 20)
* @param scale précision à appliquer sur le ht retourné
* @return le ht avec la précision scale
*/
public static final BigDecimal getHtFromTtc(BigDecimal ttc,
BigDecimal taxe, int scale) {
public static final BigDecimal getHtFromTtc(BigDecimal ttc, BigDecimal taxe, int scale) {
if (taxe.signum() == 0) {
return ttc.setScale(scale, RoundingMode.HALF_UP);
}
 
BigDecimal tauxB = taxe.movePointLeft(2).add(BigDecimal.ONE);
BigDecimal result = ttc.divide(tauxB, DecimalUtils.HIGH_PRECISION).setScale(
scale, RoundingMode.HALF_UP);
BigDecimal result = ttc.divide(tauxB, DecimalUtils.HIGH_PRECISION).setScale(scale, RoundingMode.HALF_UP);
return result;
 
}
67,8 → 57,7
 
public static void main(String[] args) {
 
BigDecimal ttcFromHt = getTtcFromHt(BigDecimal.ONE, new BigDecimal(20),
6);
BigDecimal ttcFromHt = getTtcFromHt(BigDecimal.ONE, new BigDecimal(20), 6);
System.err.println(ttcFromHt);
System.err.println(getHtFromTtc(ttcFromHt, new BigDecimal(20), 6));
 
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/HeadlessGestion.java
32,30 → 32,29
try {
this.comptaPropsConfiguration.getUserManager().setCurrentUserID(userId);
this.comptaPropsConfiguration.setUpSocieteDataBaseConnexion(companyId);
// finish filling the configuration before going any further, otherwise the
// SQLElementDirectory is not coherent
this.comptaPropsConfiguration.getModuleManager().init();
} catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException("Unable to configure connection for userId: " + userId + " companyId: " + companyId);
 
throw new IllegalStateException("Unable to configure connection for userId: " + userId + " companyId: " + companyId, e);
}
return this;
}
 
public HeadlessGestion setupGlobalState(int userId, int companyId) {
setupGlobalState(userId, companyId);
// setUpSocieteDataBaseConnexion() called by setup() needs the directory and elements need
// Configuration.getInstance()
setGlobalState();
setup(userId, companyId);
return this;
}
 
public HeadlessGestion setGlobalState() {
System.setProperty("java.awt.headless", "true");
 
TranslationManager.getInstance().addTranslationStreamFromClass(MainFrame.class);
TranslationManager.getInstance().setLocale(Locale.getDefault());
// TODO remove
Configuration.setInstance(this.comptaPropsConfiguration);
 
System.out.println("HeadlessOpenConcerto ready");
System.out.println(
"Connected to " + this.comptaPropsConfiguration.getServerIp() + " on " + this.comptaPropsConfiguration.getSystemRootName() + "/" + this.comptaPropsConfiguration.getSocieteBaseName());
return this;
}
 
/trunk/OpenConcerto/src/org/openconcerto/erp/graph/GraphArticleMargePanel.java
42,11 → 42,7
sel.addSelect(tableVFElement.getField("QTE"), "SUM");
 
@SuppressWarnings("unchecked")
final List<Object[]> rowsArticle = (List<Object[]>) Configuration
.getInstance()
.getBase()
.getDataSource()
.execute(
final List<Object[]> rowsArticle = (List<Object[]>) Configuration.getInstance().getBase().getDataSource().execute(
sel.asString() + " GROUP BY \"SAISIE_VENTE_FACTURE_ELEMENT\".\"" + field + "\"" + ",\"SAISIE_VENTE_FACTURE_ELEMENT\".\"PA_HT\"" + ",\"SAISIE_VENTE_FACTURE_ELEMENT\".\"PV_HT\"",
new ArrayListHandler());
 
/trunk/OpenConcerto/src/org/openconcerto/erp/importer/DataImporter.java
20,6 → 20,7
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesCluster.StoreMode;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.users.UserManager;
116,10 → 117,10
 
public void commit() throws SQLException {
for (SQLRowValues row : this.valuesToInsert) {
row.insert();
row.getGraph().store(StoreMode.INSERT, false);
}
for (SQLRowValues row : this.valuesToUpdate) {
row.update();
row.getGraph().store(StoreMode.COMMIT, false);
}
doAfterImport();
}
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/logo_paypal_106x29.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/logo_paypal_106x29.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/PayPalPreferencePanel.java
New file
0,0 → 1,280
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.erp.preferences;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.preferences.SQLPreferences;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.preferences.JavaPrefPreferencePanel;
import org.openconcerto.ui.preferences.PrefView;
import org.openconcerto.utils.Base64;
import org.openconcerto.utils.JImage;
import org.openconcerto.utils.PrefType;
 
import java.awt.Desktop;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
 
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
 
public class PayPalPreferencePanel extends JavaPrefPreferencePanel {
 
public static final String PAYPAL_INVOICE = "paypal.invoice";
public static final String PAYPAL_CLIENTID = "paypal.clientid";
public static final String PAYPAL_SECRET = "paypal.secret";
public static final String PAYPAL_INVOICE_X = "paypal.invoice.x";
public static final String PAYPAL_INVOICE_Y = "paypal.invoice.y";
 
// Usage :
// SQLPreferences prefs = SQLPreferences.getMemCached(comptaConf.getRootSociete());
// prefs.getBoolean(PayPalPreferencePanel.PAYPAL_INVOICE, false));
 
public PayPalPreferencePanel() {
super("Paypal", null);
 
setPrefs(new SQLPreferences(((ComptaPropsConfiguration) Configuration.getInstance()).getRootSociete()));
this.removeAll();
 
this.setLayout(new GridBagLayout());
GridBagConstraints c = new DefaultGridBagConstraints();
// Logo
c.gridwidth = 3;
c.weightx = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
final JImage img = new JImage(PayPalPreferencePanel.class.getResource("logo_paypal_106x29.png"));
try {
img.setHyperLink(new URI("https://www.paypal.fr"));
} catch (URISyntaxException e2) {
// No hyperlink
}
this.add(img, c);
c.gridy++;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 3;
this.add(new JLabel("Intégration du lien de paiement PayPal dans les factures PDF"), c);
c.gridy++;
this.add(new JLabel("Cette fonctionnalité nécessite la création d'un compte professionnel (gratuit)."), c);
// Account creation
final JButton b = new JButton("Créer votre compte maintenant");
b.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (Desktop.isDesktopSupported()) {
final String url = "https://www.paypal.com/fr/merchantsignup/create";
try {
Desktop.getDesktop().browse(new URI(url));
} catch (IOException | URISyntaxException e1) {
JOptionPane.showMessageDialog(PayPalPreferencePanel.this, url, "Erreur d'ouverture du navigateur", JOptionPane.WARNING_MESSAGE);
}
}
 
}
});
c.gridy++;
c.fill = GridBagConstraints.NONE;
this.add(b, c);
 
c.gridy++;
this.add(new JLabel(" "), c);
c.gridy++;
this.add(new JLabelBold("Paramètres de l'API"), c);
c.gridy++;
this.add(new JLabel("à créer depuis https://developer.paypal.com"), c);
 
// Client ID
c.gridx = 0;
c.gridy++;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 1;
c.weightx = 0;
this.add(new JLabel("Client ID", SwingConstants.RIGHT), c);
c.gridwidth = 2;
c.gridx++;
c.weightx = 1;
PrefView<String> clientIdPrefView = new PrefView<>(PrefType.STRING_TYPE, "Client ID", PAYPAL_CLIENTID);
this.addView(clientIdPrefView);
this.add(clientIdPrefView.getVW().getComp(), c);
 
// Secret
c.gridx = 0;
c.gridy++;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 1;
c.weightx = 0;
this.add(new JLabel("Secret", SwingConstants.RIGHT), c);
c.gridwidth = 2;
c.gridx++;
c.weightx = 1;
PrefView<String> secretPrefView = new PrefView<>(PrefType.STRING_TYPE, "Secret", PAYPAL_SECRET);
this.addView(secretPrefView);
this.add(secretPrefView.getVW().getComp(), c);
 
// Enable
c.gridx = 0;
c.gridy++;
c.fill = GridBagConstraints.NONE;
c.gridwidth = 3;
c.weightx = 0;
c.anchor = GridBagConstraints.EAST;
final JButton bVerify = new JButton("Vérifier les paramètres");
this.add(bVerify, c);
// Enable
c.gridx = 1;
c.gridy++;
c.fill = GridBagConstraints.NONE;
c.gridwidth = 1;
c.weightx = 0;
c.anchor = GridBagConstraints.EAST;
PrefView<Boolean> enabledPrefView = new PrefView<>(PrefType.BOOLEAN_TYPE, "intégration", PAYPAL_INVOICE);
this.addView(enabledPrefView);
final JComponent comp = enabledPrefView.getVW().getComp();
((JCheckBox) comp).setText("intégrer le lien de paiement dans les factures PDF");
this.add(comp, c);
 
// Position
c.gridy++;
c.fill = GridBagConstraints.HORIZONTAL;
this.add(new JLabel(" "), c);
c.gridy++;
c.gridx = 0;
c.gridwidth = 3;
this.add(new JLabelBold("Position du logo sur le PDF"), c);
 
c.gridx = 0;
c.gridy++;
c.gridwidth = 1;
c.weightx = 0;
this.add(new JLabel("X (0 - 440)", SwingConstants.RIGHT), c);
c.gridx++;
PrefView<Integer> xPrefView = new PrefView<>(PrefType.INT_TYPE, "X", PAYPAL_INVOICE_X);
xPrefView.setDefaultValue(10);
this.addView(xPrefView);
this.add(xPrefView.getVW().getComp(), c);
 
c.gridx = 0;
c.gridy++;
c.gridwidth = 1;
c.weightx = 0;
this.add(new JLabel("Y (0 - 780)", SwingConstants.RIGHT), c);
c.gridx++;
PrefView<Integer> yPrefView = new PrefView<>(PrefType.INT_TYPE, "Y", PAYPAL_INVOICE_Y);
yPrefView.setDefaultValue(10);
this.addView(yPrefView);
this.add(yPrefView.getVW().getComp(), c);
c.gridy++;
c.weighty = 1;
JPanel spacer = new JPanel();
this.add(spacer, c);
 
bVerify.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
boolean ok = false;
try {
ok = sendAuth(clientIdPrefView.getVW().getValue(), secretPrefView.getVW().getValue());
} catch (IOException e1) {
e1.printStackTrace();
}
if (ok) {
JOptionPane.showMessageDialog(PayPalPreferencePanel.this, "Paramètres OK", "API Paypal", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(PayPalPreferencePanel.this, "Paramètres incorrects", "API Paypal", JOptionPane.ERROR_MESSAGE);
}
}
});
 
}
 
@Override
protected void addViews() {
// Nothing to do here, panel is built in the constructor
}
 
public static void main(String[] args) throws IOException {
String id = "Abb2mKqK0TSU6Jgf71CJjFx0u5x6_NmEzHduuvsCXjYCfYxRg9GZ7B6ptx3pijriuq9Apx9Jp-VTFveF";
String s = "ENR1EyKreAlPbLRI0ofm2NbW6nyk8W5cJUZ7mvaBMgoQ6gB6VBivZpQW6B96toCtLZ3ClCUVutN-Gal2";
sendAuth(id, s + "");
}
 
static boolean sendAuth(String clientId, String secret) throws IOException {
final URL url = new URL("https://api.paypal.com/v1/oauth2/token");
final URLConnection con = url.openConnection();
final HttpURLConnection http = (HttpURLConnection) con;
http.setRequestMethod("POST");
http.setDoOutput(true);
http.setDefaultUseCaches(false);
String authString = clientId + ":" + secret;
String authStringEnc = Base64.encodeBytes(authString.getBytes(), Base64.DONT_BREAK_LINES);
con.setRequestProperty("Authorization", "Basic " + authStringEnc);
 
// x-www-form-urlencoded
final Map<String, String> arguments = new HashMap<>();
arguments.put("grant_type", "client_credentials");
final StringJoiner sj = new StringJoiner("&");
for (Map.Entry<String, String> entry : arguments.entrySet()) {
sj.add(URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8"));
}
byte[] out = sj.toString().getBytes(StandardCharsets.UTF_8);
int length = out.length;
http.setFixedLengthStreamingMode(length);
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
http.connect();
try (OutputStream os = http.getOutputStream()) {
os.write(out);
}
if (http.getResponseCode() == 401) {
return false;
}
 
InputStream is = http.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
 
int numCharsRead;
char[] charArray = new char[1024];
StringBuilder sb = new StringBuilder();
while ((numCharsRead = isr.read(charArray)) > 0) {
sb.append(charArray, 0, numCharsRead);
}
String result = sb.toString();
return result.contains("access_token");
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/GestionCommercialeGlobalPreferencePanel.java
40,6 → 40,7
public static String CREATE_ECR_CHQ = "CreateEcritureCheque";
public static String CHIFFRAGE_COMMANDE_CLIENT = "ChiffrageCmdClient";
public static String IMPUT_ECART = "ImputEcart";
public static String FRAIS_DOCUMENT = "GestionFraisDocuments";
 
public GestionCommercialeGlobalPreferencePanel() {
super("Gestion des pièces commerciales", null);
88,6 → 89,10
ecoColumns.setDefaultValue(Boolean.FALSE);
this.addView(ecoColumns);
 
PrefView<Boolean> fraisDoc = new PrefView<Boolean>(PrefType.BOOLEAN_TYPE, "Gérer des frais sur la création des documents clients", FRAIS_DOCUMENT);
fraisDoc.setDefaultValue(Boolean.FALSE);
this.addView(fraisDoc);
 
PrefView<Boolean> ecoTotal = new PrefView<Boolean>(PrefType.BOOLEAN_TYPE, "Afficher le total de l'éco-contribution", AbstractArticleItemTable.SHOW_TOTAL_ECO_CONTRIBUTION);
ecoTotal.setDefaultValue(Boolean.FALSE);
this.addView(ecoTotal);
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/SocietePreferencePanel.java
19,13 → 19,21
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.preferences.DefaultPreferencePanel;
import org.openconcerto.utils.checks.ValidListener;
import org.openconcerto.utils.checks.ValidObject;
import org.openconcerto.utils.checks.ValidState;
 
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
 
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
 
public class SocietePreferencePanel extends DefaultPreferencePanel {
 
private SQLComponent sc;
private boolean valid = true;
private String validText = "";
 
public SocietePreferencePanel() {
this.setLayout(new GridBagLayout());
40,7 → 48,15
this.sc.uiInit();
this.sc.select(((ComptaPropsConfiguration) Configuration.getInstance()).getSocieteID());
this.add(this.sc, c);
this.sc.addValidListener(new ValidListener() {
 
@Override
public void validChange(ValidObject src, ValidState newValue) {
valid = newValue.isValid();
validText = newValue.getValidationText();
}
});
}
 
public String getTitleName() {
return "Société";
47,10 → 63,27
}
 
public void storeValues() {
if (valid) {
this.sc.update();
} else {
final String t;
if (this.validText == null) {
t = "valeurs non valides";
} else {
t = this.validText;
}
SwingUtilities.invokeLater(new Runnable() {
 
public void restoreToDefaults() {
@Override
public void run() {
JOptionPane.showMessageDialog(SocietePreferencePanel.this, t);
}
});
 
}
}
 
public void restoreToDefaults() {
// nothing
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/GestionArticleGlobalPreferencePanel.java
17,6 → 17,7
package org.openconcerto.erp.preferences;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.core.common.ui.AbstractVenteArticleItemTable;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.preferences.SQLPreferences;
import org.openconcerto.ui.preferences.JavaPrefPreferencePanel;
35,6 → 36,7
public static String SHOW_PRODUCT_BAR_CODE = "ShowProductBarCode";
public static String ITEM_PACKAGING = "ItemPackaging";
public static String FILTER_BY_FAMILY = "FilterByFamily";
public static String STOCK_MULTI_DEPOT = "MultiDepot";
public static String CAN_EXPAND_NOMENCLATURE_VT = "CanExpandNomenclature";
public static String CAN_EXPAND_NOMENCLATURE_HA = "CanExpandNomenclaturePurchase";
 
46,6 → 48,10
@Override
protected void addViews() {
 
PrefView<Boolean> viewShowDevise = new PrefView<Boolean>(PrefType.BOOLEAN_TYPE, "Gérer plusieurs devises", AbstractVenteArticleItemTable.ARTICLE_SHOW_DEVISE);
viewShowDevise.setDefaultValue(Boolean.FALSE);
this.addView(viewShowDevise);
 
PrefView<Boolean> viewExpandNom = new PrefView<Boolean>(PrefType.BOOLEAN_TYPE, "Permettre d'applatir, d'exposer, d'éclater les articles dans les pièces commerciales de vente",
CAN_EXPAND_NOMENCLATURE_VT);
viewExpandNom.setDefaultValue(Boolean.TRUE);
72,6 → 78,10
view.setDefaultValue(Boolean.TRUE);
this.addView(view);
 
PrefView<Boolean> viewMultiStock = new PrefView<Boolean>(PrefType.BOOLEAN_TYPE, "Activer la gestion multidépôt", STOCK_MULTI_DEPOT);
viewMultiStock.setDefaultValue(Boolean.FALSE);
this.addView(viewMultiStock);
 
PrefView<Boolean> view2 = new PrefView<Boolean>(PrefType.BOOLEAN_TYPE, "Gérer différentes unités de vente", UNITE_VENTE);
view2.setDefaultValue(Boolean.TRUE);
this.addView(view2);
/trunk/OpenConcerto/src/org/openconcerto/erp/preferences/TemplatePreferencePanel.java
13,11 → 13,11
package org.openconcerto.erp.preferences;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.AbstractLocalTemplateProvider;
import org.openconcerto.erp.generationDoc.DefaultLocalTemplateProvider;
import org.openconcerto.erp.generationDoc.TemplateManager;
import org.openconcerto.erp.generationDoc.TemplateProvider;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.preferences.DefaultPreferencePanel;
48,7 → 48,6
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
57,7 → 56,6
public class TemplatePreferencePanel extends DefaultPreferencePanel {
 
private JTextField textTemplate;
private JFileChooser fileChooser = null;
 
private JButton bModify;
private JButton bUndo;
71,18 → 69,36
/*******************************************************************************************
* Emplacement
******************************************************************************************/
if (!ComptaPropsConfiguration.getInstanceCompta().isOnCloud()) {
this.add(new JLabel("Modèles des documents"), cPanel);
cPanel.gridx++;
cPanel.weightx = 1;
this.textTemplate = new JTextField();
this.add(this.textTemplate, cPanel);
 
this.textTemplate.setEditable(false);
final JButton buttonTemplate = new JButton("...");
cPanel.gridx++;
cPanel.weightx = 0;
cPanel.fill = GridBagConstraints.NONE;
this.add(buttonTemplate, cPanel);
buttonTemplate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setCurrentDirectory(new File(TemplatePreferencePanel.this.textTemplate.getText()));
if (fileChooser.showDialog(TemplatePreferencePanel.this, "Sélectionner") == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
if (selectedFile.exists()) {
TemplatePreferencePanel.this.textTemplate.setForeground(UIManager.getColor("TextField.foreground"));
} else {
TemplatePreferencePanel.this.textTemplate.setForeground(Color.RED);
}
TemplatePreferencePanel.this.textTemplate.setText(selectedFile.getPath());
}
 
}
});
}
final JPanel templates = createTemplateList();
templates.setOpaque(false);
cPanel.gridy++;
93,13 → 109,6
cPanel.gridx = 0;
this.add(templates, cPanel);
 
buttonTemplate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
directoryChoose("template");
}
});
this.textTemplate.setEditable(false);
 
setValues();
}
 
118,7 → 127,11
bUndo.setOpaque(false);
c.gridx++;
p.add(bUndo, c);
if (ComptaPropsConfiguration.getInstanceCompta().isOnCloud()) {
bSync = new JButton("Synchroniser vers le cloud");
} else {
bSync = new JButton("Synchroniser");
}
bSync.setEnabled(false);
bSync.setOpaque(false);
if (!UserRightsManager.getCurrentUserRights().haveRight("SYNC_TEMPLATE")) {
175,10 → 188,18
if (provider instanceof AbstractLocalTemplateProvider) {
final String templateId = dm.getTemplateId(row);
File f = ((AbstractLocalTemplateProvider) provider).getFileTemplate(templateId, null, null);
String path = "invalid path";
if (f != null) {
try {
path = f.getCanonicalPath();
} catch (IOException e) {
path = f.getAbsolutePath();
}
}
if (f == null || !f.exists()) {
tableCellRendererComponent.setBackground(Color.ORANGE);
if (f != null) {
setToolTipText(f.getAbsolutePath() + " not found");
setToolTipText(path + " not found");
} else {
setToolTipText("no file for template " + templateId);
}
188,7 → 209,7
} else {
tableCellRendererComponent.setBackground(table.getBackground());
}
setToolTipText(f.getAbsolutePath());
setToolTipText(path);
}
}
return tableCellRendererComponent;
256,7 → 277,7
}
 
public void storeValues() {
 
if (this.textTemplate != null) {
final File z = new File(".");
final File f = new File(this.textTemplate.getText());
try {
272,6 → 293,7
 
TemplateNXProps.getInstance().store();
}
}
 
public void restoreToDefaults() {
 
282,6 → 304,7
}
 
private void setValues() {
if (this.textTemplate != null) {
try {
final File f = new File(TemplateNXProps.getInstance().getStringProperty("LocationTemplate"));
if (f.exists()) {
295,33 → 318,8
e.printStackTrace();
}
}
 
private void directoryChoose(final String type) {
 
if (this.fileChooser == null) {
this.fileChooser = new JFileChooser();
this.fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
this.fileChooser.setCurrentDirectory(new File(TemplatePreferencePanel.this.textTemplate.getText()));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
 
if (TemplatePreferencePanel.this.fileChooser.showDialog(TemplatePreferencePanel.this, "Sélectionner") == JFileChooser.APPROVE_OPTION) {
 
if (type.equalsIgnoreCase("template")) {
File selectedFile = TemplatePreferencePanel.this.fileChooser.getSelectedFile();
if (selectedFile.exists()) {
TemplatePreferencePanel.this.textTemplate.setForeground(UIManager.getColor("TextField.foreground"));
} else {
TemplatePreferencePanel.this.textTemplate.setForeground(Color.RED);
}
TemplatePreferencePanel.this.textTemplate.setText(selectedFile.getPath());
}
}
}
});
}
 
public void modifyTemplate(final TemplateTableModel dm, final JTable table) {
int row = table.getSelectedRow();
dm.unsync(row);
/trunk/OpenConcerto/src/org/openconcerto/erp/config/SQLElementNames_en.xml
File deleted
/trunk/OpenConcerto/src/org/openconcerto/erp/config/SQLElementNames_fr.xml
File deleted
/trunk/OpenConcerto/src/org/openconcerto/erp/config/SQLElementNames_pl.xml
File deleted
/trunk/OpenConcerto/src/org/openconcerto/erp/config/InstallationPanel.java
19,6 → 19,7
import org.openconcerto.erp.core.sales.quote.element.EtatDevisSQLElement;
import org.openconcerto.erp.modules.ModuleManager;
import org.openconcerto.erp.modules.ModuleReference;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.changer.convert.AddFK;
import org.openconcerto.sql.changer.convert.ChangeIDToInt;
import org.openconcerto.sql.changer.correct.CorrectOrder;
138,6 → 139,20
System.setProperty(SQLSchema.NOAUTO_CREATE_METADATA, "false");
final ComptaPropsConfiguration conf = ComptaPropsConfiguration.create(true);
 
final DBSystemRoot systemRoot = conf.getSystemRoot();
if (systemRoot.getChild(conf.getRootNameValue().getValue()) == null) {
conf.destroy();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
up.setEnabled(true);
bar.setValue(bar.getMaximum());
JOptionPane.showMessageDialog(InstallationPanel.this, "Votre base de données n'est pas initialisée");
}
});
return;
}
 
updateDatabase(conf);
 
conf.destroy();
198,7 → 213,6
 
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
try {
if (finderPanel.getServerConfig().createUserIfNeeded(login.getText(), mdp.getText())) {
JOptionPane.showMessageDialog(InstallationPanel.this, "L'utilisateur openconcerto a été correctement ajouté.");
206,7 → 220,6
JOptionPane.showMessageDialog(InstallationPanel.this, "L'utilisateur openconcerto existe déjà dans la base.");
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
JOptionPane.showMessageDialog(InstallationPanel.this, "Une erreur est survenue pendant la connexion au serveur, vérifiez vos paramètres de connexion.");
}
882,11 → 895,14
for (final SQLTable t : root.getTables()) {
final AlterTable alter = new AlterTable(t);
for (final SQLField f : t.getFields()) {
if (f.getType().getType() == Types.VARCHAR && f.getType().getSize() == Integer.MAX_VALUE) {
// on PG, CLOB are text and the JDBC driver returns Types.VARCHAR, not CLOB. So test
// if the type name contains "char", since we only want unbounded varchar, not
// text/clob.
if (f.getType().getType() == Types.VARCHAR && f.getType().getSize() == Integer.MAX_VALUE && f.getTypeDecl().contains("char")) {
 
UpdateBuilder build = new UpdateBuilder(t);
build.set(f.getName(), "''");
build.setWhere(new Where(f, "=", (Object) null));
build.setObject(f.getName(), "");
build.setWhere(Where.isNull(f));
builds.add(build);
 
final String fName = f.getName();
1241,7 → 1257,190
checkDepartementExists(root, "Aisne", "Laon", "Picardie", "02");
 
if (root.contains("TARIF_AGENCE")) {
SQLTable tableCmdFA = root.getTable("COMMANDE");
if (!tableCmdFA.contains("INCOTERM")) {
AlterTable t = new AlterTable(tableCmdFA);
t.addVarCharColumn("INCOTERM", 256);
tableCmdFA.getBase().getDataSource().execute(t.asString());
tableCmdFA.getSchema().updateVersion();
tableCmdFA.fetchFields();
}
 
SQLTable tableArticleA = root.getTable("ARTICLE");
if (!tableArticleA.contains("CLOSED_KIT")) {
AlterTable t = new AlterTable(tableArticleA);
t.addBooleanColumn("CLOSED_KIT", Boolean.FALSE, true);
tableArticleA.getBase().getDataSource().execute(t.asString());
tableArticleA.getSchema().updateVersion();
tableArticleA.fetchFields();
}
 
if (!tableArticleA.contains("CLOSED_KIT_DESC")) {
AlterTable t = new AlterTable(tableArticleA);
t.addVarCharColumn("CLOSED_KIT_DESC", 4096);
tableArticleA.getBase().getDataSource().execute(t.asString());
tableArticleA.getSchema().updateVersion();
tableArticleA.fetchFields();
}
 
SQLTable tableCmdFEltA = root.getTable("COMMANDE_ELEMENT");
if (!tableCmdFEltA.contains("INCOTERM")) {
AlterTable t = new AlterTable(tableCmdFEltA);
t.addVarCharColumn("INCOTERM", 256);
tableCmdFEltA.getBase().getDataSource().execute(t.asString());
tableCmdFEltA.getSchema().updateVersion();
tableCmdFEltA.fetchFields();
}
 
if (root.getTable("VILLE") == null) {
 
// INSERT INTO "OpenConcerto49"."VILLE" ("CODE_POSTAL","NOM") SELECT
// "CODE_POSTAL",
// "VILLE" FROM "OpenConcerto49"."ADRESSE" a WHERE a."ARCHIVE"=0
final SQLCreateTable createTableVille = new SQLCreateTable(root, "VILLE");
createTableVille.addVarCharColumn("NOM", 2048);
createTableVille.addVarCharColumn("CODE_POSTAL", 2048);
createTableVille.addLongColumn("X_LAMBERT", 0L, true);
createTableVille.addLongColumn("Y_LAMBERT", 0L, true);
createTableVille.addLongColumn("POPULATION", 0L, true);
try {
root.getBase().getDataSource().execute(createTableVille.asString());
insertUndef(createTableVille);
root.refetchTable("VILLE");
root.getSchema().updateVersion();
} catch (SQLException ex2) {
throw new IllegalStateException("Erreur lors de la création de la table " + "VILLE", ex2);
}
}
 
if (root.getTable("ARTICLE_PRIX_MIN_VENTE") == null) {
final SQLCreateTable createTablePrixMin = new SQLCreateTable(root, "ARTICLE_PRIX_MIN_VENTE");
createTablePrixMin.addForeignColumn("ARTICLE");
createTablePrixMin.addIntegerColumn("QTE", 1);
createTablePrixMin.addDecimalColumn("PRIX", 16, 8, BigDecimal.ZERO, true);
createTablePrixMin.addDateAndTimeColumn("DATE");
try {
root.getBase().getDataSource().execute(createTablePrixMin.asString());
insertUndef(createTablePrixMin);
root.refetchTable("ARTICLE_PRIX_MIN_VENTE");
root.getSchema().updateVersion();
} catch (SQLException ex2) {
throw new IllegalStateException("Erreur lors de la création de la table " + "ARTICLE_PRIX_MIN_VENTE", ex2);
}
}
 
{
final SQLTable tableTarifAgence = root.getTable("TARIF_AGENCE");
AlterTable tTarifAgence = new AlterTable(tableTarifAgence);
boolean updateTarifAgence = false;
 
if (!tableTarifAgence.contains("PRC_AGENCE")) {
updateTarifAgence = true;
tTarifAgence.addDecimalColumn("PRC_AGENCE", 16, 8, BigDecimal.ZERO, true);
}
if (!tableTarifAgence.contains("CLOSED_KIT")) {
updateTarifAgence = true;
tTarifAgence.addBooleanColumn("CLOSED_KIT", false, true);
}
if (!tableTarifAgence.contains("PRIX_MIN_VENTE")) {
updateTarifAgence = true;
tTarifAgence.addDecimalColumn("PRIX_MIN_VENTE", 16, 8, BigDecimal.ZERO, true);
}
 
if (updateTarifAgence) {
tableTarifAgence.getBase().getDataSource().execute(tTarifAgence.asString());
tableTarifAgence.getSchema().updateVersion();
tableTarifAgence.fetchFields();
}
}
{
final SQLTable tableClient = root.getTable("CLIENT");
AlterTable tClient = new AlterTable(tableClient);
boolean updateClient = false;
 
if (!tableClient.contains("ALG_REGISTRE")) {
updateClient = true;
tClient.addVarCharColumn("ALG_REGISTRE", 512);
}
if (!tableClient.contains("ALG_MATRICULE")) {
updateClient = true;
tClient.addVarCharColumn("ALG_MATRICULE", 512);
}
if (!tableClient.contains("ALG_ARTICLE")) {
updateClient = true;
tClient.addVarCharColumn("ALG_ARTICLE", 512);
}
 
if (updateClient) {
tableClient.getBase().getDataSource().execute(tClient.asString());
tableClient.getSchema().updateVersion();
tableClient.fetchFields();
}
}
final SQLTable tableMvtStock = root.getTable("MOUVEMENT_STOCK");
 
if (!tableMvtStock.contains("PRICE")) {
AlterTable tMvt = new AlterTable(tableMvtStock);
tMvt.addDecimalColumn("PRICE", 16, 6, BigDecimal.ZERO, true);
tableMvtStock.getBase().getDataSource().execute(tMvt.asString());
tableMvtStock.getSchema().updateVersion();
tableMvtStock.fetchFields();
}
 
final SQLTable tableArticle = root.getTable("ARTICLE");
 
if (!tableArticle.contains("AUTO_PRIX_MIN_VENTE_NOMENCLATURE")) {
AlterTable tArt = new AlterTable(tableArticle);
tArt.addBooleanColumn("AUTO_PRIX_MIN_VENTE_NOMENCLATURE", false, true);
tableMvtStock.getBase().getDataSource().execute(tArt.asString());
tableMvtStock.getSchema().updateVersion();
tableMvtStock.fetchFields();
}
SQLTable tableContact = root.getTable("CONTACT");
if (!tableContact.contains("TYPE")) {
AlterTable t = new AlterTable(tableContact);
t.addVarCharColumn("TYPE", 256);
t.addVarCharColumn("SERVICE", 256);
t.addVarCharColumn("PAYS", 256);
t.addForeignColumn("ID_ADRESSE", root.getTable("ADRESSE"));
tableContact.getBase().getDataSource().execute(t.asString());
tableContact.getSchema().updateVersion();
tableContact.fetchFields();
}
 
if (!root.contains("PERSONNEL_AFHYMAT")) {
final SQLCreateTable createTablePers = new SQLCreateTable(root, "PERSONNEL_AFHYMAT");
createTablePers.addVarCharColumn("NOM", 256);
createTablePers.addVarCharColumn("PRENOM", 256);
createTablePers.addVarCharColumn("FONCTION", 256);
 
try {
root.getBase().getDataSource().execute(createTablePers.asString());
insertUndef(createTablePers);
root.refetchTable("PERSONNEL_AFHYMAT");
root.getSchema().updateVersion();
} catch (SQLException ex) {
throw new IllegalStateException("Erreur lors de la création de la table " + "PERSONNEL_AFHYMAT", ex);
}
 
final SQLCreateTable createTable = new SQLCreateTable(root, "PERSONNEL_AFHYMAT_COUT");
for (int i = 1; i <= 12; i++) {
createTable.addDecimalColumn("SAL_MONTH_" + i, 16, 2, BigDecimal.ZERO, false);
createTable.addDecimalColumn("COM_MONTH_" + i, 16, 2, BigDecimal.ZERO, false);
}
createTable.addIntegerColumn("ANNEE", 0);
createTable.addForeignColumn("ID_PERSONNEL_AFHYMAT", root.getTable("PERSONNEL_AFHYMAT"));
 
try {
root.getBase().getDataSource().execute(createTable.asString());
insertUndef(createTable);
root.refetchTable("PERSONNEL_AFHYMAT_COUT");
root.getSchema().updateVersion();
} catch (SQLException ex) {
throw new IllegalStateException("Erreur lors de la création de la table " + "PERSONNEL_AFHYMAT_COUT", ex);
}
}
 
List<String> tablesUiLocked = Arrays.asList("BON_DE_LIVRAISON", "COMMANDE_CLIENT", "SAISIE_VENTE_FACTURE", "AVOIR_CLIENT", "COMMANDE", "BON_RECEPTION", "FACTURE_FOURNISSEUR");
for (String tableName : tablesUiLocked) {
final SQLTable table = root.getTable(tableName);
1277,7 → 1476,6
}
 
if (!root.getTable("ARTICLE").contains("TRANSPORT")) {
final SQLTable tableArticle = root.getTable("ARTICLE");
AlterTable alterArticle = new AlterTable(tableArticle);
alterArticle.addBooleanColumn("TRANSPORT", false, false);
tableArticle.getBase().getDataSource().execute(alterArticle.asString());
1307,7 → 1505,6
 
{
if (root.getName().endsWith("54")) {
final SQLTable tableArticle = root.getTable("ARTICLE");
AlterTable tArt = new AlterTable(tableArticle);
if (!tableArticle.contains("LABEL_DOUANE")) {
tArt.addVarCharColumn("LABEL_DOUANE", 512);
2165,6 → 2362,7
root.getDBSystemRoot().getDataSource().execute(createTable.asString());
insertUndef(createTable);
tableDevis.getSchema().updateVersion();
root.refetchTable("DEVISE_HISTORIQUE");
} catch (SQLException ex) {
throw new IllegalStateException("Erreur lors de la création de la table TASK", ex);
}
3360,7 → 3558,7
patchFieldElt1Dot3(table, root);
}
 
private void updateSocieteSchema(final DBRoot root) throws IOException, Exception {
private void updateSocieteSchema(final Configuration conf, final DBRoot root) throws IOException, Exception {
final DBSystemRoot sysRoot = root.getDBSystemRoot();
final SQLDataSource ds = sysRoot.getDataSource();
System.out.println("InstallationPanel.InstallationPanel() UPDATE COMMERCIAL " + root);
3464,7 → 3662,7
final List<ChangeTable<?>> changes = new ArrayList<ChangeTable<?>>();
 
final ModuleManager instance = new ModuleManager();
instance.setRoot(root);
instance.setup(root, conf);
final Collection<ModuleReference> refs = instance.getModulesInstalledRemotely();
final Set<String> allUsedTable = new HashSet<String>();
for (ModuleReference ref : refs) {
3473,6 → 3671,10
}
System.out.println("Tables created by modules:" + allUsedTable);
 
final Set<String> notMigrated = instance.migrateOldTransientDirs();
if (!notMigrated.isEmpty())
System.out.println("Couldn't migrate old backed up/failed modules: " + notMigrated);
 
final List<String> alterRequests = ChangeTable.cat(changes, root.getName());
try {
for (final String req : alterRequests) {
3956,24 → 4158,27
}
 
public void updateDatabase(final ComptaPropsConfiguration conf) {
 
try {
final SQLDataSource ds = conf.getSystemRoot().getDataSource();
System.err.println("SystemRoot:" + conf.getSystemRoot());
System.err.println("Root:" + conf.getRoot());
final DBSystemRoot systemRoot = conf.getSystemRoot();
System.err.println("SystemRoot:" + systemRoot);
final DBRoot rootCommon = conf.getRoot();
System.err.println("Root:" + rootCommon);
 
final SQLDataSource ds = systemRoot.getDataSource();
// FixUnbounded varchar
fixUnboundedVarchar(conf.getRoot());
fixUnboundedVarchar(rootCommon);
 
// FIXME DROP CONSTRAINT UNIQUE ORDRE ON
// CONTACT_FOURNISSEUR
 
checkCompteDefault(conf.getRoot());
checkCompteDefault(rootCommon);
 
updateSocieteTable(conf.getRoot());
updateVille(conf.getRoot().getTable("ADRESSE"));
updateSocieteTable(rootCommon);
updateVille(rootCommon.getTable("ADRESSE"));
 
// Champ user 1.4.1
final SQLTable tableUser = conf.getRoot().getTable("USER_COMMON");
final SQLTable tableUser = rootCommon.getTable("USER_COMMON");
if (tableUser != null) {
final AlterTable alter = new AlterTable(tableUser);
boolean alterUser = false;
3988,32 → 4193,32
 
if (alterUser) {
final String req = alter.asString();
conf.getRoot().getDBSystemRoot().getDataSource().execute(req);
conf.getRoot().refetchTable(tableUser.getName());
conf.getRoot().getSchema().updateVersion();
rootCommon.getDBSystemRoot().getDataSource().execute(req);
rootCommon.refetchTable(tableUser.getName());
rootCommon.getSchema().updateVersion();
}
}
 
// Champ Paye
final SQLTable tableCaisse = conf.getRoot().getTable("CAISSE_COTISATION");
final SQLTable tabl