OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 79 → Rev 80

/trunk/OpenConcerto/.classpath
20,16 → 20,17
<classpathentry exported="true" kind="lib" path="lib/ognl-2.6.5.jar"/>
<classpathentry exported="true" kind="lib" path="lib/resolver.jar"/>
<classpathentry exported="true" kind="lib" path="lib/RXTXcomm.jar"/>
<classpathentry exported="true" kind="lib" path="lib/xercesImpl.jar"/>
<classpathentry exported="true" kind="lib" path="lib/poi-3.8-beta3-20110606.jar"/>
<classpathentry exported="true" kind="lib" path="lib/jsch-0.1.44.jar"/>
<classpathentry kind="lib" path="lib/jcip-annotations.jar"/>
<classpathentry kind="lib" path="lib/fb-annotations-2.0.0.jar"/>
<classpathentry kind="lib" path="lib/jOpenDocument-1.3b2.jar"/>
<classpathentry kind="lib" path="lib/h2-1.3.168.jar"/>
<classpathentry exported="true" kind="lib" path="lib/commons-dbcp-1.4.jar"/>
<classpathentry kind="lib" path="lib/commons-pool-1.6.jar"/>
<classpathentry kind="lib" path="lib/icu4j-51_2.jar"/>
<classpathentry kind="lib" path="lib/postgresql-9.2-1003.jdbc4.jar"/>
<classpathentry kind="lib" path="lib/h2-1.3.175.jar"/>
<classpathentry kind="lib" path="lib/icu4j-51_2-data-western_europe.jar"/>
<classpathentry kind="lib" path="lib/icu4j-51_2-module_format.jar"/>
<classpathentry kind="lib" path="lib/jOpenDocument-1.3.jar"/>
<classpathentry kind="lib" path="lib/xercesImpl-2.9.1.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
/trunk/OpenConcerto/lib/icu4j-51_2.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/h2-1.3.168.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/jOpenDocument-1.3b2.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/xercesImpl.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/icu4j-51_2-data-western_europe.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/icu4j-51_2-data-western_europe.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/jOpenDocument-1.3.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/jOpenDocument-1.3.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/xercesImpl-2.9.1.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/xercesImpl-2.9.1.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/h2-1.3.175.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/h2-1.3.175.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/icu4j-51_2-module_format.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/icu4j-51_2-module_format.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/src/org/jopenchart/sample/devguide/GuideFrame.java
3,6 → 3,7
import java.awt.Container;
import java.awt.Dimension;
 
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
50,7 → 51,7
guidePanel2
.addURL("http://chart.apis.google.com/chart?cht=s&chd=s:984sttvuvkQIBLKNCAIi,DEJPgq0uov17zwopQODS,AFLPTXaflptx159gsDrn&chxt=x,y&chxl=0:|0|2|3|4|5|6|7|8|9|10|1:|0|25|50|75|100&chs=200x125");
JScrollPane scrollPane = new JScrollPane(guidePanel2);
scrollPane.setBorder(null);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.add("Charts type", scrollPane);
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/convert/ChangeIDToInt.java
New file
0,0 → 1,54
/*
* 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.changer.convert;
 
import org.openconcerto.sql.changer.Changer;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLField.Properties;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.utils.AlterTable;
 
import java.sql.SQLException;
import java.sql.Types;
import java.util.EnumSet;
 
/**
* Change the type of the primary key to int.
*
* @author Sylvain CUAZ
*/
public class ChangeIDToInt extends Changer<SQLTable> {
 
public ChangeIDToInt(DBSystemRoot b) {
super(b);
}
 
@Override
protected void changeImpl(final SQLTable t) throws SQLException {
this.getStream().print(t + "... ");
if (!t.isRowable()) {
this.getStream().println("not rowable");
} else {
final SQLField pk = t.getKey();
if (pk.getType().getType() == Types.INTEGER) {
this.getStream().println("already");
} else {
getDS().execute(new AlterTable(t).alterColumn(pk.getName(), EnumSet.of(Properties.TYPE), "int", null, null).asString());
t.getSchema().updateVersion();
this.getStream().println("done");
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/convert/SetFKDefault.java
New file
0,0 → 1,82
/*
* 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.changer.convert;
 
import org.openconcerto.sql.changer.Changer;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.utils.AlterTable;
import org.openconcerto.utils.CompareUtils;
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
 
/**
* Set missing default value for foreign fields.
*
* @author Sylvain
*/
public class SetFKDefault extends Changer<SQLTable> {
 
public SetFKDefault(DBSystemRoot b) {
super(b);
}
 
@Override
protected void changeImpl(SQLTable t) throws SQLException {
final Set<Link> foreignLinks = t.getDBSystemRoot().getGraph().getForeignLinks(t);
final AlterTable alter = new AlterTable(t);
for (final Link link : foreignLinks) {
final int colCount = link.getCols().size();
final SQLTable target = link.getTarget();
 
final List<String> correctDefaults;
final SQLField singleField = link.getSingleField();
if (singleField != null && link.getRefFields().get(0).isPrimaryKey()) {
// common case
final Number undefinedID = target.getUndefinedIDNumber();
correctDefaults = Collections.singletonList(undefinedID == null ? null : singleField.getType().toString(undefinedID));
} else {
// general case
correctDefaults = new ArrayList<String>(colCount);
for (final SQLField rf : link.getRefFields()) {
// from DatabaseMetadata.getColumns() : COLUMN_DEF is a string, i.e. SQL value
correctDefaults.add((String) rf.getDefaultValue());
}
}
assert correctDefaults.size() == colCount;
 
int i = 0;
for (final SQLField ff : link.getFields()) {
final String correctDef = correctDefaults.get(i);
// don't overwrite
if (ff.getDefaultValue() == null && !CompareUtils.equals(ff.getDefaultValue(), correctDef)) {
getStream().println("will change " + ff + " from " + ff.getDefaultValue() + " to " + correctDef);
alter.alterColumnDefault(ff.getName(), correctDef);
}
i++;
}
assert i == colCount;
}
if (!alter.isEmpty()) {
this.getDS().execute(alter.asString());
t.getSchema().updateVersion();
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/convert/OrderToDecimal.java
48,6 → 48,7
} else
throw new IllegalStateException("not implemented for " + system);
this.getDS().execute(update);
t.getSchema().updateVersion();
getStream().println("done");
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/correct/FixSerial.java
16,24 → 16,21
import org.openconcerto.sql.changer.Changer;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
 
import java.util.EnumSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class FixSerial extends Changer<SQLTable> {
 
// nextVal('"SCHEMA"."seqName"'::regclass);
private final static Pattern seqPattern = Pattern.compile("nextval\\('(.+)'.*\\)");
 
public FixSerial(DBSystemRoot b) {
super(b);
}
 
protected EnumSet<SQLSystem> getCompatibleSystems() {
// not needed on H2, see Column.updateSequenceIfRequired()
return EnumSet.of(SQLSystem.POSTGRESQL);
}
 
49,9 → 46,9
if (!t.isRowable()) {
getStream().println("not rowable");
} else {
final String seqName = getPrimaryKeySeq(t);
final SQLName seqName = getPrimaryKeySeq(t);
if (seqName != null) {
final SQLSelect sel = new SQLSelect(t.getBase(), true);
final SQLSelect sel = new SQLSelect(true);
sel.addSelect(t.getKey(), "max");
final Number maxID = (Number) this.getDS().executeScalar(sel.asString());
// begin at 1 if table is empty
59,7 → 56,7
// for some reason this doesn't always work (maybe a cache pb ?):
// final String s = "SELECT setval('" + seqName + "', " + maxID + ")";
// while this does
final String s = "ALTER SEQUENCE " + seqName + " RESTART " + nextID;
final String s = "ALTER SEQUENCE " + seqName.quote() + " RESTART WITH " + nextID;
this.getDS().executeScalar(s);
getStream().println("done");
} else
67,36 → 64,16
}
}
 
static public String getPrimaryKeySeq(SQLTable t) throws IllegalStateException {
static public SQLName getPrimaryKeySeq(SQLTable t) throws IllegalStateException {
if (!t.isRowable()) {
return null;
} else {
return getSeq(t.getKey());
}
}
 
/**
* Return the name of the sequence for the default value of <code>f</code>.
*
* @param f a field.
* @return the name of the sequence or <code>null</code> if the default is not a sequence, eg
* "schema"."table_id_seq".
* @throws IllegalStateException if the sequence couldn't be parsed.
*/
static public String getSeq(SQLField f) throws IllegalStateException {
if (f == null) {
final SQLField key = t.getKey();
if (key == null) {
return null;
} else {
final String def = ((String) f.getDefaultValue()).trim();
if (def.startsWith("nextval")) {
final Matcher matcher = seqPattern.matcher(def);
if (matcher.matches()) {
return matcher.group(1);
} else
throw new IllegalStateException("could not parse: " + def + " with " + seqPattern.pattern());
} else
return null;
return key.getOwnedSequence();
}
}
 
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/correct/CorrectOrder.java
70,6 → 70,7
final String order = table.getOrderField().getName();
final AlterTable alter = new AlterTable(table).addUniqueConstraint(table.getName() + "_order", singletonList(order));
getDS().execute(alter.asString());
table.getSchema().updateVersion();
this.getStream().println("unique on " + order + " added");
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/listview/ListItemSQLView.java
56,7 → 56,7
c.weightx = 0;
// Minimize/Maximize
if (this.enableMinimize) {
final JImageToggleButton expandButton = new JImageToggleButton(this.getClass().getResource("down.png"), this.getClass().getResource("right.png"));
final JImageToggleButton expandButton = new JImageToggleButton(ListItemSQLView.class.getResource("down.png"), ListItemSQLView.class.getResource("right.png"));
this.add(expandButton, c);
c.gridx++;
expandButton.addActionListener(new ActionListener() {
81,7 → 81,7
this.add(separator, c);
c.gridx++;
// Close
this.supprBtn = new JButton(new ImageIcon(this.getClass().getResource("/ilm/sql/element/delete.png")));
this.supprBtn = new JButton(new ImageIcon(ListItemSQLView.class.getResource("/ilm/sql/element/delete.png")));
this.supprBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ListItemSQLView.this.parent.removeItem(ListItemSQLView.this);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/EditFrame.java
240,8 → 240,12
 
@Deprecated
public void selectionId(int selectedId, int i) {
// otherwise when the user inserts, the component is partialReset(), then the row gets in
// the list and is selected, and this method overwrite the reset values.
if (!this.getPanel().alwaysVisible()) {
this.getPanel().selectionId(selectedId, i);
}
}
 
public void selectionId(int selectedId) {
this.getPanel().selectionId(selectedId, 0);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/EditPanel.java
52,6 → 52,7
import java.util.Vector;
 
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
66,6 → 67,7
* @author ilm Created on 8 oct. 2003
*/
public class EditPanel extends JPanel implements IListener, ActionListener, Documented {
 
public static enum EditMode {
CREATION {
@Override
93,6 → 95,14
* Otherwise they're added after the selection.
*/
public final static String ADD_AT_THE_END = "org.openconcerto.sql.editPanel.endAdd";
/**
* If this system property is true, then the scroll pane won't have a border.
*/
public static final String NOBORDER = "org.openconcerto.editpanel.noborder";
/**
* If this system property is true, add a separator before the buttons.
*/
public static final String ADD_SEPARATOR = "org.openconcerto.editpanel.separator";
 
public final static EditMode CREATION = EditMode.CREATION;
public final static EditMode MODIFICATION = EditMode.MODIFICATION;
142,7 → 152,7
* <li>to modify if MODIFICATION</li>
* <li>to view if READONLY</li>
* </ul>
* use "org.openconcerto.editpanel.noborder" and "org.openconcerto.editpanel.separator" for custom style
* use {@value #NOBORDER} and {@value #ADD_SEPARATOR} for custom style
*
* @param e the element to display.
* @param mode the edit mode, one of CREATION, MODIFICATION or READONLY.
181,19 → 191,23
updateBtns();
}
});
((BaseSQLComponent) this.component).addSelectionListener(new PropertyChangeListener() {
final PropertyChangeListener updateBtnsListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateBtns();
}
});
};
// update buttons if the selection changes of row or if the current row changes
((BaseSQLComponent) this.component).addSelectionListener(updateBtnsListener);
this.getSQLComponent().addPropertyChangeListener(SQLComponent.READ_ONLY_PROP, updateBtnsListener);
}
 
this.uiInit();
this.component.uiInit();
 
if (Boolean.getBoolean("org.openconcerto.editpanel.noborder")) {
this.setInnerBorder(null);
if (Boolean.getBoolean(NOBORDER)) {
// don't use null, it will be replaced by updateUI()
this.setInnerBorder(BorderFactory.createEmptyBorder());
}
} catch (Exception ex) {
ExceptionHandler.handle(TM.tr("init.error"), ex);
213,12 → 227,15
final UserRights rights = UserRightsManager.getCurrentUserRights();
if (!TableAllRights.hasRight(rights, code, getSQLComponent().getElement().getTable())) {
res = ValidState.createCached(false, TM.tr(desc));
} else if (needID && !idOK)
} else if (this.getSQLComponent().isSelectionReadOnly()) {
res = ValidState.createCached(false, TM.tr("editPanel.readOnlySelection"));
} else if (needID && !idOK) {
res = ValidState.createCached(false, TM.tr("editPanel.inexistentElement"));
else if (needValid && !this.valid.isValid())
} else if (needValid && !this.valid.isValid()) {
res = this.valid;
else
} else {
res = ValidState.getTrueInstance();
}
updateBtn(b, res);
}
}
227,10 → 244,10
this.fill();
 
// les raccourcis claviers
this.component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK), "add");
this.component.getActionMap().put("add", new AbstractAction() {
this.component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK), "apply");
this.component.getActionMap().put("apply", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
ajouter();
apply();
}
});
}
264,7 → 281,7
container.add(this.p, c);
 
// Separator, if needed
if (Boolean.getBoolean("org.openconcerto.editpanel.separator")) {
if (Boolean.getBoolean(ADD_SEPARATOR)) {
c.gridy++;
c.weightx = 1;
c.weighty = 0;
292,7 → 309,6
if (!Boolean.getBoolean("org.openconcerto.editpanel.hideKeepOpen")) {
container.add(this.keepOpen, c);
}
this.keepOpen.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.gridx = 2;
c.anchor = GridBagConstraints.EAST;
316,7 → 332,6
}
}
});
 
} else if (this.mode == MODIFICATION) {
c.gridx = 1;
c.anchor = GridBagConstraints.EAST;
383,6 → 398,21
}
}
 
protected final void apply() {
final JButton b;
if (this.mode == CREATION)
b = this.jButtonAjouter;
else if (this.mode == MODIFICATION)
b = this.jButtonModifier;
else if (this.mode == READONLY)
b = this.jButtonAnnuler;
else
b = null;
 
if (b != null)
b.doClick();
}
 
/*
* (non-Javadoc)
*
443,6 → 473,10
id = this.component.insert();
if (this.l != null)
this.l.selectID(id);
// otherwise full reset on visibility change.
if (this.alwaysVisible()) {
((BaseSQLComponent) this.getSQLComponent()).partialReset();
}
this.fireInserted(id);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/TabbedIListPanel.java
53,6 → 53,12
}
 
@Override
protected boolean modifyIsImmediate() {
// tab is displayed
return false;
}
 
@Override
protected void listSelectionChanged(int id) {
super.listSelectionChanged(id);
if (tabbedPanel == null)
/trunk/OpenConcerto/src/org/openconcerto/sql/view/ListeModifyPanel.java
100,6 → 100,11
}
 
@Override
protected boolean modifyIsImmediate() {
return true;
}
 
@Override
protected void listSelectionChanged(int id) {
super.listSelectionChanged(id);
// keep the current scrolling value, to restore it afterwards
/trunk/OpenConcerto/src/org/openconcerto/sql/view/ListeAddPanel.java
54,6 → 54,12
}
 
@Override
protected boolean modifyIsImmediate() {
// EditFrame is displayed
return false;
}
 
@Override
public SQLComponent getModifComp() {
return this.getEditFrame().getPanel().getSQLComponent();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/IListButton.java
22,6 → 22,7
import java.awt.Dimension;
 
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
86,7 → 87,7
}
 
public static final void initButton(JButton b) {
b.setBorder(null);
b.setBorder(BorderFactory.createEmptyBorder());
b.setOpaque(false);
b.setContentAreaFilled(false);
b.setFocusPainted(false);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/QuickAssignRowValuesTablePanel.java
New file
0,0 → 1,41
/*
* 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.view;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.view.list.RowValuesTable;
import org.openconcerto.sql.view.list.RowValuesTableModel;
import org.openconcerto.sql.view.list.RowValuesTablePanel;
 
public class QuickAssignRowValuesTablePanel extends RowValuesTablePanel {
public QuickAssignRowValuesTablePanel(RowValuesTableModel model) {
if (model == null) {
throw new IllegalArgumentException("null RowValuesTableModel");
}
this.model = model;
init();
uiInit();
}
 
@Override
protected void init() {
this.table = new RowValuesTable(model, null);
this.table.setEditable(false);
}
 
@Override
public SQLElement getSQLElement() {
return model.getSQLElement();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/SearchItemComponent.java
40,6 → 40,7
import java.util.SortedMap;
import java.util.TreeMap;
 
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
158,7 → 159,7
// supprime un element de recherche
c.gridx++;
this.buttonRemove.setIcon(new ImageIcon(BaseSQLComponent.class.getResource("delete.png")));
this.buttonRemove.setBorder(null);
this.buttonRemove.setBorder(BorderFactory.createEmptyBorder());
this.buttonRemove.setOpaque(false);
this.buttonRemove.setBorderPainted(false);
this.buttonRemove.setFocusPainted(false);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowValuesTable.java
85,7 → 85,7
}
 
public RowValuesTable(RowValuesTableModel model, File f, boolean tiny) {
this(model, f, false, new XTableColumnModel());
this(model, f, tiny, new XTableColumnModel());
}
 
public RowValuesTable(RowValuesTableModel model, File f, boolean tiny, XTableColumnModel colModel) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ListSQLLine.java
119,8 → 119,10
}
 
public final void updateValueAt(Set<Integer> colIndexes) {
if (colIndexes.size() == 0)
return;
final int max = Collections.max(colIndexes).intValue();
synchronized (this) {
final int max = Collections.max(colIndexes).intValue();
final int alreadyLoaded = this.loadedCol;
this.loadCache(max);
for (final int colIndex : colIndexes) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSource.java
115,10 → 115,10
 
@Override
public Set<FieldPath> getPaths() {
return FieldPath.create(new Path(getPrimaryTable()), getPrimaryTable().getFieldsName());
return FieldPath.create(Path.get(getPrimaryTable()), getPrimaryTable().getFieldsName());
}
});
this.allCols.add(new SQLTableModelColumnPath(new Path(getPrimaryTable()), getPrimaryTable().getKey().getName(), "PrimaryKey"));
this.allCols.add(new SQLTableModelColumnPath(Path.get(getPrimaryTable()), getPrimaryTable().getKey().getName(), "PrimaryKey"));
 
// at the end so that fireColsChanged() can use it
this.inited = null;
220,6 → 220,13
return Collections.unmodifiableList(this.allCols);
}
 
public final void addDebugColumn(final SQLTableModelColumn col) {
this.init();
this.allCols.add(col);
colsChanged(new ListChangeIndex.Add<SQLTableModelColumn>(this.allCols.size() - 1, Collections.singleton(col)));
this.fireColsChanged();
}
 
public final SQLTableModelColumn getColumn(int index) {
return this.getAllColumns().get(index);
}
258,6 → 265,26
return res;
}
 
/**
* The column depending solely on the passed path.
*
* @param fp the field path.
* @return the column needing only <code>fp</code>.
* @throws IllegalArgumentException if more than one column matches.
*/
public final SQLTableModelColumn getColumn(FieldPath fp) {
final Set<FieldPath> singleton = Collections.singleton(fp);
SQLTableModelColumn res = null;
for (final SQLTableModelColumn col : this.getColumns())
if (col.getPaths().equals(singleton)) {
if (res == null)
res = col;
else
throw new IllegalArgumentException("Not exactly one column for " + fp);
}
return res;
}
 
public final void addColumnListener(PropertyChangeListener l) {
this.supp.addPropertyChangeListener("cols", l);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/MoveQueue.java
27,6 → 27,7
import org.openconcerto.utils.DecimalUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.SleepingQueue;
import org.openconcerto.utils.Tuple2;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
81,6 → 82,43
});
}
 
// row index as returned by JTable.DropLocation.getRow()
public void moveTo(final List<? extends SQLRowAccessor> rows, final int rowIndex) {
if (rows.size() == 0)
return;
 
this.put(new Runnable() {
public void run() {
final FutureTask<Tuple2<Boolean, ListSQLLine>> future = new FutureTask<Tuple2<Boolean, ListSQLLine>>(new Callable<Tuple2<Boolean, ListSQLLine>>() {
@Override
public Tuple2<Boolean, ListSQLLine> call() {
assert rowIndex >= 0;
final int rowCount = MoveQueue.this.tableModel.getRowCount();
final boolean after = rowIndex >= rowCount;
final int index = after ? rowCount - 1 : rowIndex;
return Tuple2.create(after, MoveQueue.this.tableModel.getRow(index));
}
});
MoveQueue.this.tableModel.invokeLater(future);
try {
final Tuple2<Boolean, ListSQLLine> line = future.get();
final boolean after = line.get0();
final List<? extends SQLRowAccessor> l = new ArrayList<SQLRowAccessor>(rows);
Collections.sort(l, after ? Collections.reverseOrder(OrderComparator.INSTANCE) : OrderComparator.INSTANCE);
SQLUtils.executeAtomic(MoveQueue.this.tableModel.getTable().getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, Exception>() {
@Override
public Object handle(SQLDataSource ds) throws Exception {
moveQuick(l, after, line.get1().getRow().asRow());
return null;
}
});
} catch (Exception e) {
throw ExceptionUtils.createExn(IllegalStateException.class, "move failed", e);
}
}
});
}
 
final void moveQuick(final List<? extends SQLRowAccessor> srcRows, final boolean after, final SQLRow destRow) throws SQLException {
final int rowCount = srcRows.size();
// if only some rows are moved, update one by one (avoids refreshing the whole list)
168,7 → 206,7
// move out before general request as most DB systems haven't got DEFERRABLE constraints
if (!after && !destRowReordered) {
final UpdateBuilder updateDestRow = new UpdateBuilder(t);
updateDestRow.set(t.getOrderField().getName(), newOrder.toPlainString());
updateDestRow.setObject(t.getOrderField(), newOrder);
updateDestRow.setWhere(destRow.getWhere());
t.getDBSystemRoot().getDataSource().execute(updateDestRow.asString());
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/UpdateOneRunnable.java
38,7 → 38,7
final CollectionMap<Path, ListSQLLine> affectedPaths = this.getAffectedPaths();
// the line should be in the list (since SQLTableModelLinesSource.get()
// returned it), so if not yet part of the list add it.
if (line != null && affectedPaths.getNonNull(new Path(getTable())).isEmpty())
if (line != null && affectedPaths.getNonNull(Path.get(getTable())).isEmpty())
line.clearCache();
// then, update affectedPaths (it's not because the changed table is the primary
// table, that it's not also referenced, e.g. CIRCUIT.ORIGINE)
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/UpdateQueue.java
19,6 → 19,7
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.view.list.UpdateRunnable.RmAllRunnable;
import org.openconcerto.utils.IFutureTask;
135,7 → 136,7
input.getAcc().add(input.getCurrent().getTable());
return input.getAcc();
}
}, RecursionType.BREADTH_FIRST, null);
}, RecursionType.BREADTH_FIRST, Direction.ANY);
return res;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListe.java
18,6 → 18,7
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.Log;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLField;
28,6 → 29,9
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.users.User;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.view.FileTransfertHandler;
import org.openconcerto.sql.view.IListener;
import org.openconcerto.sql.view.list.IListeAction.ButtonsBuilder;
42,6 → 46,7
import org.openconcerto.ui.MenuUtils;
import org.openconcerto.ui.PopupMouseListener;
import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.ui.list.selection.BaseListStateModel;
import org.openconcerto.ui.list.selection.ListSelection;
import org.openconcerto.ui.list.selection.ListSelectionState;
import org.openconcerto.ui.state.JTableStateManager;
102,6 → 107,7
 
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DropMode;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
121,6 → 127,7
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
136,11 → 143,62
*/
public final class IListe extends JPanel {
 
static private final class LockAction extends RowAction {
public LockAction(final boolean lock) {
super(new AbstractAction(TM.tr(lock ? "ilist.lockRows" : "ilist.unlockRows")) {
@Override
public void actionPerformed(ActionEvent e) {
final IListe list = IListe.get(e);
final List<Integer> ids = list.getSelection().getSelectedIDs();
final SQLTable t = list.getSource().getPrimaryTable();
final UpdateBuilder update = new UpdateBuilder(t);
update.setObject(SQLComponent.READ_ONLY_FIELD, lock ? SQLComponent.READ_ONLY_VALUE : SQLComponent.READ_WRITE_VALUE);
final User user = UserManager.getUser();
if (user != null)
update.setObject(SQLComponent.READ_ONLY_USER_FIELD, user.getId());
update.setWhere(new Where(t.getKey(), ids));
t.getDBSystemRoot().getDataSource().execute(update.asString());
// don't fire too many times, as each one will cause UpdateQueue to issue a
// request
final Collection<? extends Number> fireIDs = ids.size() < 12 ? ids : Collections.singleton(SQLRow.NONEXISTANT_ID);
for (final Number fireID : fireIDs)
t.fireTableModified(fireID.intValue(), update.getFieldsNames());
}
}, false, true);
}
 
@Override
public boolean enabledFor(IListeEvent evt) {
// TODO use right
return evt.getSelectedRows().size() > 0;
}
}
 
private static LockAction LOCK_ACTION;
private static LockAction UNLOCK_ACTION;
 
private static final LockAction getLockAction() {
assert SwingUtilities.isEventDispatchThread();
// don't create too early as we might not have the localisation available. Further some
// applications will never use it.
if (LOCK_ACTION == null)
LOCK_ACTION = new LockAction(true);
return LOCK_ACTION;
}
 
private static final LockAction getUnlockAction() {
assert SwingUtilities.isEventDispatchThread();
if (UNLOCK_ACTION == null)
UNLOCK_ACTION = new LockAction(false);
return UNLOCK_ACTION;
}
 
/**
* When this system property is set, table {@link JTableStateManager state} is never read nor
* written. I.e. the user can change the table state but it will be reset at each launch.
*/
public static final String STATELESS_TABLE_PROP = "org.openconcerto.sql.list.statelessTable";
private static final String SELECTION_DATA_PROPNAME = "selectionData";
 
static private final class FormatRenderer extends DefaultTableCellRenderer {
private final Format fmt;
158,10 → 216,21
 
private static boolean FORCE_ALT_CELL_RENDERER = false;
static final String SEP = " ► ";
public static final TableCellRenderer DATE_RENDERER = new FormatRenderer(DateFormat.getDateInstance(DateFormat.MEDIUM));
public static final TableCellRenderer TIME_RENDERER = new FormatRenderer(DateFormat.getTimeInstance(DateFormat.SHORT));
public static final TableCellRenderer DATETIME_RENDERER = new FormatRenderer(DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT));
 
// DefaultTableCellRenderer is stateful, so safer to not share (JTable also has private
// instances, see createDefaultRenderers())
public static final TableCellRenderer createDateRenderer() {
return new FormatRenderer(DateFormat.getDateInstance(DateFormat.MEDIUM));
}
 
public static final TableCellRenderer createTimeRenderer() {
return new FormatRenderer(DateFormat.getTimeInstance(DateFormat.SHORT));
}
 
public static final TableCellRenderer createDateTimeRenderer() {
return new FormatRenderer(DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT));
}
 
private static final Map<Class<?>, FormatGroup> FORMATS;
static {
FORMATS = new HashMap<Class<?>, FormatGroup>();
225,6 → 294,7
private final PropertyChangeSupport supp;
// for not adjusting listeners
private final ListSelectionListener selectionListener;
private final TableModelListener selectionDataListener;
// filter
private final PropertyChangeListener filterListener;
// listen on model's properties
291,6 → 361,7
final String modif = getLine(false, row, getSource().getPrimaryTable().getModifUserField(), getSource().getPrimaryTable().getModifDateField());
if (modif != null)
infoL.add(modif);
// TODO locked by
 
final String info;
if (infoL.size() == 0)
336,6 → 407,11
this.debugFilter = false;
this.filterWorker = null;
 
// DnD
this.jTable.setDragEnabled(true);
this.jTable.setDropMode(DropMode.INSERT_ROWS);
this.jTable.setTransferHandler(new IListeTransferHandler());
 
// do not handle F2, let our application use it :
// remove F2 keybinding, use space
final InputMap tm = this.jTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
372,6 → 448,38
}
}
};
this.selectionDataListener = new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
// assert we're not listening to the sorter since we're interested in data change
// not sort order
assert e.getSource() instanceof ITableModel;
boolean fire = false;
// insert or delete don't change the content of current selection (e.g. if the
// deleted row was part of the selection "selectedIDs" will change)
// a change in the name or order of columns doesn't mean the SQL values are updated
if (e.getType() == TableModelEvent.UPDATE && e.getFirstRow() != TableModelEvent.HEADER_ROW) {
// see TableModelEvent(TableModel) constructor
if (e.getLastRow() == Integer.MAX_VALUE) {
// since JTable uses a regular listener to update its selection and the
// listeners are called in reverse order, the selection isn't yet cleared by
// JTable.tableChanged(). Thus if the table was just shrunk, the selection
// might be out of bounds. So don't fire now, let
// JTable.clearSelectionAndLeadAnchor() do it.
fire = false;
} else {
// do fire if only some rows were updated as in this case, no selection
// change will occur.
for (int i = e.getFirstRow(); !fire && i <= e.getLastRow(); i++) {
if (getJTable().getSelectionModel().isSelectedIndex(IListe.this.sorter.viewIndex(i)))
fire = true;
}
}
}
if (fire)
IListe.this.supp.firePropertyChange(SELECTION_DATA_PROPNAME, null, null);
}
};
this.filterListener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
updateFilter();
409,6 → 517,13
fireSelectionId(((Number) evt.getNewValue()).intValue(), IListe.this.jTable.getSelectedColumn());
}
});
// don't use userSelectedIDs as we need to fire when the whole list is changed, see
// this.selectionDataListener
this.state.addPropertyChangeListener("selectedIDs", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
IListe.this.supp.firePropertyChange(SELECTION_DATA_PROPNAME, null, null);
}
});
// this.jTable.setEnabled(!updating) ne sert à rien
// car les updates du ITableModel se font de manière synchrone dans la EDT
// donc on ne peut faire aucune action pendant les maj
654,9 → 769,9
return super.getTableCellRendererComponent(table, StringClobConvertor.INSTANCE.unconvert((Clob) value), isSelected, hasFocus, row, column);
}
});
this.jTable.setDefaultRenderer(Date.class, DATE_RENDERER);
this.jTable.setDefaultRenderer(Time.class, TIME_RENDERER);
this.jTable.setDefaultRenderer(Timestamp.class, DATETIME_RENDERER);
this.jTable.setDefaultRenderer(Date.class, createDateRenderer());
this.jTable.setDefaultRenderer(Time.class, createTimeRenderer());
this.jTable.setDefaultRenderer(Timestamp.class, createDateTimeRenderer());
for (final Map.Entry<Class<?>, FormatGroup> e : this.getFormats().entrySet())
this.jTable.setDefaultEditor(e.getKey(), new FormatEditor(e.getValue()));
this.sorter.setTableHeader(this.jTable.getTableHeader());
733,7 → 848,12
 
this.setOpaque(false);
this.setTransferHandler(new FileTransfertHandler(getSource().getPrimaryTable()));
 
if (this.getSource().getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_FIELD) != null) {
this.addIListeAction(getUnlockAction());
this.addIListeAction(getLockAction());
}
}
 
protected synchronized final void invertDebug() {
this.setDebug(!this.debugFilter);
915,7 → 1035,21
return ITableModel.getLine(this.getJTable().getModel(), viewIndex);
}
 
private SQLRow getRow(int id) {
// protect our internal values
private <R> R getRow(int index, final Class<R> clazz) {
final SQLRowValues internalRow = this.getLine(index).getRow();
final SQLRowAccessor toCast;
if (clazz == SQLRowValues.class) {
toCast = internalRow.deepCopy();
} else if (clazz == SQLRow.class) {
toCast = internalRow.asRow();
} else {
toCast = new SQLImmutableRowValues(internalRow);
}
return clazz.cast(toCast);
}
 
private SQLRow fetchRow(int id) {
if (id < SQLRow.MIN_VALID_ID) {
return null;
} else
922,12 → 1056,30
return this.getSource().getPrimaryTable().getRow(id);
}
 
public SQLRow getSelectedRow() {
return this.getRow(this.getSelectedId());
public SQLRow fetchSelectedRow() {
return this.fetchRow(this.getSelectedId());
}
 
public SQLRowAccessor getSelectedRow() {
return this.getSelectedRow(SQLRowAccessor.class);
}
 
public SQLRowValues copySelectedRow() {
return this.getSelectedRow(SQLRowValues.class);
}
 
// selected row cannot be inferred from iterateSelectedRows() since the user might have selected
// the last row anywhere in the selection
private final <R extends SQLRowAccessor> R getSelectedRow(final Class<R> clazz) {
final int selectedIndex = this.state.getSelectedIndex().intValue();
if (selectedIndex == BaseListStateModel.INVALID_INDEX)
return null;
else
return this.getRow(selectedIndex, clazz);
}
 
public final SQLRow getDesiredRow() {
return this.getRow(this.getSelection().getUserSelectedID());
return this.fetchRow(this.getSelection().getUserSelectedID());
}
 
public final List<SQLRowAccessor> getSelectedRows() {
948,8 → 1100,7
final List<R> res = new ArrayList<R>();
for (int i = start; i <= stop; i++) {
if (selectionModel.isSelectedIndex(i)) {
final SQLRowValues internalRow = this.getLine(i).getRow();
res.add(clazz.cast(clazz == SQLRowValues.class ? internalRow.deepCopy() : new SQLImmutableRowValues(internalRow)));
res.add(getRow(i, clazz));
}
}
return res;
984,10 → 1135,22
this.naListeners.add(l);
}
 
/**
* Adds a listener to the list that's notified each time a change to the data model occurs. This
* includes when this is not displayable and the model becomes empty.
*
* @param l the listener.
* @see #retain()
*/
public void addListener(TableModelListener l) {
this.jTable.getModel().addTableModelListener(l);
// sorter is final, only its own model (ITableModel) changes
this.sorter.addTableModelListener(l);
}
 
public void removeListener(TableModelListener l) {
this.sorter.removeTableModelListener(l);
}
 
/**
* To be notified when the table is being sorted. Each time a sort is requested you'll be
* notified twice to indicate the beginning and end of the sort. Don't confuse it with the
1009,6 → 1172,14
return this.sorter.isSorting();
}
 
public final void setSortingEnabled(final boolean b) {
this.sorter.setSortingEnabled(b);
}
 
public final boolean isSortingEnabled() {
return this.sorter.isSortingEnabled();
}
 
private void fireSelectionId(int id, int selectedColumn) {
for (IListener l : this.listeners) {
l.selectionId(id, selectedColumn);
1050,6 → 1221,21
getModel().rmPropertyChangeListener(l);
}
 
/**
* Listen to the content of the selection, i.e. both selection ID change and data change of the
* current selection. Note: <code>l</code> is called for each selection change, even when
* {@link ListSelectionEvent#getValueIsAdjusting()} is <code>true</code>.
*
* @param l the listener.
*/
public final void addSelectionDataListener(final PropertyChangeListener l) {
this.supp.addPropertyChangeListener(SELECTION_DATA_PROPNAME, l);
}
 
public final void removeSelectionDataListener(final PropertyChangeListener l) {
this.supp.removePropertyChangeListener(SELECTION_DATA_PROPNAME, l);
}
 
protected final void visibilityChanged() {
// test isDead() since in JComponent.removeNotify() first setDisplayable(false) (in super)
// then firePropertyChange("ancestor", null).
1087,6 → 1273,7
if (old != null) {
for (final PropertyChangeListener l : this.modelPCListeners)
old.rmPropertyChangeListener(l);
old.removeTableModelListener(this.selectionDataListener);
if (this.hasRequest())
this.getRequest().rmWhereListener(this.filterListener);
}
1101,6 → 1288,9
// signal to the listeners that the model has changed (ie all of its properties)
l.propertyChange(new PropertyChangeEvent(t, null, null, null));
}
// listen to the SQL model and not this.sorter since change in sorting doesn't change
// the selection nor its data. Full listener since not all values are displayed.
t.addTableModelListener(this.selectionDataListener, true);
if (this.hasRequest()) {
this.getRequest().addWhereListener(this.filterListener);
// the where might have changed since we last listened
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListeTransferHandler.java
New file
0,0 → 1,101
/*
* 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.view.list;
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.ui.SwingThreadUtils;
 
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
import javax.swing.JComponent;
import javax.swing.TransferHandler;
 
public class IListeTransferHandler extends TransferHandler {
static private final class Data {
private final IListe list;
private final List<SQLRowAccessor> selection;
 
private Data(IListe list, List<SQLRowAccessor> selection) {
super();
this.list = list;
this.selection = new ArrayList<SQLRowAccessor>(selection);
}
}
 
static private final DataFlavor FLAVOR = new DataFlavor(Data.class, "List of rows");
 
@Override
public int getSourceActions(JComponent c) {
return MOVE;
}
 
@Override
protected Transferable createTransferable(JComponent c) {
final IListe list = SwingThreadUtils.getAncestorOrSelf(IListe.class, c);
if (!list.getModel().isEditable() || list.isSorted() || !list.getSource().getPrimaryTable().isOrdered())
return null;
final List<SQLRowAccessor> selection = list.getSelectedRows();
return new Transferable() {
 
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { FLAVOR };
}
 
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return FLAVOR.equals(flavor);
}
 
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (!this.isDataFlavorSupported(flavor))
throw new UnsupportedFlavorException(flavor);
return new Data(list, selection);
}
};
}
 
@Override
protected void exportDone(JComponent c, Transferable t, int action) {
}
 
@Override
public boolean canImport(TransferSupport support) {
final IListe targetList = SwingThreadUtils.getAncestorOrSelf(IListe.class, support.getComponent());
return support.isDataFlavorSupported(FLAVOR) && targetList == getData(support).list;
}
 
private Data getData(TransferSupport support) {
try {
return (Data) support.getTransferable().getTransferData(FLAVOR);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
 
@Override
public boolean importData(TransferSupport support) {
if (!this.canImport(support))
return false;
final Data data = getData(support);
data.list.getModel().moveTo(data.selection, ((javax.swing.JTable.DropLocation) support.getDropLocation()).getRow());
return true;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/AbstractUpdateOneRunnable.java
64,6 → 64,11
// keep only what has changed, eg CONTACT.NOM
proto.retainAll(getModifedFields());
}
// the modified fields aren't used at the path (e.g. if we display a row and its
// same-table origin, the event was added by UpdateQueue.rowModified() since the
// the modified fields are displayed for the primary row, but might not for the
// origin)
if (!proto.getFields().isEmpty()) {
// fetch the changed rowValues
// ATTN this doesn't use the original fetcher that was used in the updateAll
// MAYBE add a slower but accurate mode using the updateAll fetcher (and thus
97,6 → 102,7
}
}
}
}
 
protected abstract Collection<String> getModifedFields();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowValuesTableModel.java
121,7 → 121,10
 
// rowParDefaut
if (addDefault) {
addRow(new SQLRowValues(this.defautRow));
final SQLRowValues row = new SQLRowValues(this.defautRow);
final BigDecimal maxOrder = RowValuesTableModel.this.element.getTable().getMaxOrder();
row.put(RowValuesTableModel.this.element.getTable().getOrderField().getName(), maxOrder.add(BigDecimal.ONE));
RowValuesTableModel.this.rowValues.add(row);
}
}
 
622,6 → 625,10
addRow(row, true);
}
 
public void submit(Runnable r) {
runnableQueue.submit(r);
}
 
public void addRow(final SQLRowValues row, final boolean fireModified) {
checkEDT();
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ITableModel.java
17,9 → 17,11
import static org.openconcerto.sql.view.list.ITableModel.SleepState.HIBERNATING;
import static org.openconcerto.sql.view.list.ITableModel.SleepState.SLEEPING;
import org.openconcerto.sql.Log;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.model.SQLFieldsSet;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.users.rights.TableAllRights;
36,6 → 38,7
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
44,6 → 47,7
import java.util.logging.Level;
 
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
114,6 → 118,7
private boolean filledOnce;
 
private final PropertyChangeSupport supp;
private List<TableModelListener> fullListeners;
 
private final UpdateQueue updateQ;
private boolean loading;
134,6 → 139,7
 
public ITableModel(SQLTableModelSource src) {
this.supp = new PropertyChangeSupport(this);
this.fullListeners = new LinkedList<TableModelListener>();
 
this.liste = new ArrayList<ListSQLLine>(100);
this.updating = false;
309,8 → 315,6
this.fireTableRowsUpdated(index, index);
else
for (final Integer i : modifiedFields) {
// only fire for currently displaying cells
if (i < this.getColumnCount())
this.fireTableCellUpdated(index, i);
}
}
331,6 → 335,18
this.setUpdating(false);
}
 
@Override
public void fireTableChanged(TableModelEvent e) {
// only fire for currently displaying cells
if (e.getColumn() == TableModelEvent.ALL_COLUMNS || e.getColumn() < this.getColumnCount()) {
super.fireTableChanged(e);
} else {
for (final TableModelListener l : this.fullListeners) {
l.tableChanged(e);
}
}
}
 
// *** tableModel
 
protected void updateColNames() {
381,18 → 397,28
return this.getReq().getColumn(columnIndex).getValueClass();
}
 
public void setEditable(boolean b) {
public final void setEditable(boolean b) {
this.editable = b;
}
 
public final boolean isEditable() {
return this.editable;
}
 
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (!this.editable)
return false;
final SQLTableModelColumn col = this.getReq().getColumn(columnIndex);
// hasRight is expensive so put it last
return col.isEditable() && hasRight(col);
return col.isEditable() && !isReadOnly(rowIndex) && hasRight(col);
}
 
private boolean isReadOnly(int rowIndex) {
final SQLRowValues r = getRow(rowIndex).getRow();
return r.getTable().contains(SQLComponent.READ_ONLY_FIELD) && SQLComponent.isReadOnly(r);
}
 
private boolean hasRight(final SQLTableModelColumn col) {
final UserRights u = UserRightsManager.getCurrentUserRights();
for (final SQLTable t : new SQLFieldsSet(col.getFields()).getTables()) {
480,6 → 506,10
this.moveQ.move(rows, inc);
}
 
public void moveTo(final List<? extends SQLRowAccessor> rows, final int rowIndex) {
this.moveQ.moveTo(rows, rowIndex);
}
 
/**
* Search the row which is <code>inc</code> lines from rowID.
*
707,13 → 737,29
return this.getClass().getSimpleName() + "@" + this.hashCode() + " for " + this.getTable();
}
 
@Override
public synchronized void addTableModelListener(TableModelListener l) {
this.addTableModelListener(l, false);
}
 
/**
* Adds a listener that's notified each time a change to the data model occurs.
*
* @param l the listener.
* @param full if <code>true</code> <code>l</code> will be notified even if the data changed
* isn't displayed ({@link #setDebug(boolean) debug columns}).
*/
public synchronized void addTableModelListener(TableModelListener l, final boolean full) {
if (this.isDead())
throw new IllegalStateException("dead tableModel: " + this);
if (full)
this.fullListeners.add(l);
super.addTableModelListener(l);
}
 
@Override
public synchronized void removeTableModelListener(TableModelListener l) {
this.fullListeners.remove(l);
super.removeTableModelListener(l);
// nobody listens to us so we die
if (this.listenerList.getListenerCount() == 0) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelColumnPath.java
37,12 → 37,14
*/
public class SQLTableModelColumnPath extends SQLTableModelColumn {
 
// try to find a RowItemDesc, fall back to SQLFieldTranslator.getDefaultDesc()
private static final RowItemDesc getDescFor(SQLField field) {
final Configuration conf = Configuration.getInstance();
if (conf == null)
final RowItemDesc res = conf == null ? SQLFieldTranslator.NULL_DESC : conf.getTranslator().getDescFor(field.getTable(), field.getName());
if (res.equals(SQLFieldTranslator.NULL_DESC))
return SQLFieldTranslator.getDefaultDesc(field);
else
return conf.getTranslator().getDescFor(field.getTable(), field.getName());
return res;
}
 
private static final String getLabelFor(SQLField field) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/search/SearchQueue.java
152,7 → 152,7
}
return input.getAcc();
}
}, RecursionType.BREADTH_FIRST, null);
}, RecursionType.BREADTH_FIRST, Direction.ANY);
for (final Path p : pathsToT) {
final String lastReferentField = getLastReferentField(p);
for (final ListSQLLine line : this.fullList) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/QuickAssignPanel.java
New file
0,0 → 1,129
/*
* 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.
*/
/*
* 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.view;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.sqlobject.ElementComboBox;
import org.openconcerto.sql.view.list.RowValuesTableModel;
import org.openconcerto.sql.view.list.RowValuesTablePanel;
import org.openconcerto.ui.DefaultGridBagConstraints;
 
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JPanel;
 
public class QuickAssignPanel extends JPanel {
 
private JButton addButton;
private JButton removeButton;
private RowValuesTablePanel itemTablePanel;
private ElementComboBox combo;
 
public QuickAssignPanel(final SQLElement element, final String uniqueIdFieldName, final RowValuesTableModel model) {
if (element == null) {
throw new IllegalArgumentException("null SQLElement");
}
if (uniqueIdFieldName == null) {
throw new IllegalArgumentException("null uniqueIdFieldName");
}
if (model == null) {
throw new IllegalArgumentException("null RowValuesTableModel");
}
 
this.setLayout(new GridBagLayout());
this.setOpaque(false);
final GridBagConstraints c = new DefaultGridBagConstraints();
c.weightx = 1;
// Toolbar
final JPanel toolbar = new JPanel(new FlowLayout(FlowLayout.LEFT));
toolbar.setOpaque(false);
combo = new ElementComboBox(false);
combo.init(element);
combo.setButtonsVisible(false);
toolbar.add(combo);
addButton = new JButton("Ajouter");
addButton.setOpaque(false);
toolbar.add(addButton);
removeButton = new JButton("Supprimer");
removeButton.setOpaque(false);
toolbar.add(removeButton, c);
this.add(toolbar, c);
 
// List
c.gridy++;
c.fill = GridBagConstraints.BOTH;
c.weighty = 1;
itemTablePanel = new QuickAssignRowValuesTablePanel(model);
itemTablePanel.setOpaque(false);
this.add(itemTablePanel, c);
 
// Listeners
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SQLRow row = combo.getSelectedRow();
if (row == null || row.isUndefined()) {
return;
}
int nbRows = itemTablePanel.getModel().getRowCount();
for (int i = 0; i < nbRows; i++) {
final SQLRowValues rowVals = itemTablePanel.getModel().getRowValuesAt(i);
final int id = Integer.parseInt(rowVals.getObject(uniqueIdFieldName).toString());
if (id == row.getID()) {
return;
}
}
itemTablePanel.getModel().addRow(combo.getSelectedRow().asRowValues());
}
});
removeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
itemTablePanel.removeSelectedRow();
}
});
}
 
public void setEnabled(boolean b) {
combo.setEnabled(b);
addButton.setEnabled(b);
removeButton.setEnabled(b);
itemTablePanel.setEnabled(b);
}
 
public RowValuesTableModel getModel() {
return itemTablePanel.getModel();
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/IListPanel.java
23,6 → 23,7
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.sqlobject.SQLRequestComboBox;
import org.openconcerto.sql.users.rights.TableAllRights;
128,7 → 129,7
final JButton res = new JButton(i);
res.setMargin(new Insets(1, 1, 1, 1));
res.setModel(new ContinuousButtonModel(300));
res.setBorder(null);
res.setBorder(BorderFactory.createEmptyBorder());
res.setOpaque(false);
res.setFocusPainted(true);
res.setContentAreaFilled(false);
205,6 → 206,13
IListPanel.this.listSelectionChanged(id);
}
});
this.liste.addSelectionDataListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
listSelectionDataChanged();
}
});
 
this.liste.addModelListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
265,16 → 273,16
 
this.buttonModifier = new JButton(TM.tr("modify"));
this.buttonModifier.setOpaque(false);
this.btnMngr.addBtn(this.buttonModifier, "noRightToModify", TableAllRights.MODIFY_ROW_TABLE, true);
this.btnMngr.addBtn(this.buttonModifier, "noRightToModify", TableAllRights.MODIFY_ROW_TABLE, true, modifyIsImmediate());
this.buttonEffacer = new JButton(TM.tr("remove"));
this.buttonEffacer.setOpaque(false);
this.btnMngr.addBtn(this.buttonEffacer, "noRightToDel", TableAllRights.DELETE_ROW_TABLE, true);
this.buttonAjouter = new JButton(TM.tr("add"));
this.buttonAjouter.setOpaque(false);
this.btnMngr.addBtn(this.buttonAjouter, "noRightToAdd", TableAllRights.ADD_ROW_TABLE, false);
this.btnMngr.addBtn(this.buttonAjouter, "noRightToAdd", TableAllRights.ADD_ROW_TABLE, false, false);
this.buttonClone = new JButton(TM.tr("duplicate"));
this.buttonClone.setOpaque(false);
this.btnMngr.addBtn(this.buttonClone, "noRightToClone", TableAllRights.ADD_ROW_TABLE, true);
this.btnMngr.addBtn(this.buttonClone, "noRightToClone", TableAllRights.ADD_ROW_TABLE, true, false);
this.btnMngr.setOKToolTip(this.buttonClone, TM.tr("listPanel.cloneToolTip"));
 
this.saveBtn = new JButton(new ImageIcon(IListPanel.class.getResource("save.png")));
335,6 → 343,8
 
}
 
protected abstract boolean modifyIsImmediate();
 
/**
* Permet aux sous classes d'ajouter d'autres composants.
*
420,7 → 430,7
}
 
private final JPanel createClonePanel(final int selectedLines, final boolean rec, final SQLRequestComboBox combo) {
final String msg = TM.tr("listPanel.cloneRows", selectedLines, rec ? 1 : 0);
final String msg = TM.getInstance().trM("listPanel.cloneRows", "rowCount", selectedLines, "rec", rec);
 
final JPanel p = new JPanel(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
559,6 → 569,11
 
// notre liste a changé de sélection
protected void listSelectionChanged(int id) {
}
 
// selection or selection content changed
protected void listSelectionDataChanged() {
// even if the same row is selected, its content can change (e.g. get locked)
this.btnMngr.updateBtns();
}
 
566,7 → 581,7
protected final class BtnTooltipMnger {
 
private final Map<JButton, Tuple2<String, String>> code;
private final Set<JButton> needSelection;
private final Set<JButton> needSelection, needRWSelection;
// btn -> tooltip
private final Map<JButton, ITransformer<JButton, String>> additional;
private final Map<JButton, String> okTooltip;
574,6 → 589,7
public BtnTooltipMnger() {
super();
this.needSelection = new HashSet<JButton>();
this.needRWSelection = new HashSet<JButton>();
this.code = new HashMap<JButton, Tuple2<String, String>>();
this.additional = new HashMap<JButton, ITransformer<JButton, String>>();
this.okTooltip = new HashMap<JButton, String>();
580,6 → 596,10
}
 
public void addBtn(final JButton btn, String desc, String rightCode, final boolean needSelection) {
this.addBtn(btn, desc, rightCode, needSelection, true);
}
 
public void addBtn(final JButton btn, String desc, String rightCode, final boolean needSelection, final boolean needRWSelection) {
// otherwise have to remove from other attributes
if (this.code.containsKey(btn))
throw new IllegalStateException("already in");
586,6 → 606,8
this.code.put(btn, Tuple2.create(desc, rightCode));
if (needSelection)
this.needSelection.add(btn);
if (needRWSelection && getElement().getTable().contains(SQLComponent.READ_ONLY_FIELD))
this.needRWSelection.add(btn);
}
 
public void setAdditional(final JButton btn, ITransformer<JButton, String> additional) {
623,6 → 645,9
if (!TableAllRights.hasRight(rights, t.get1(), getElement().getTable())) {
ok = false;
tooltip = TM.tr(t.get0());
} else if (this.needRWSelection.contains(btn) && isRO()) {
ok = false;
tooltip = TM.tr("editPanel.readOnlySelection");
} else if (this.needSelection.contains(btn) && !hasSelection) {
ok = false;
tooltip = TM.tr("noSelection");
637,7 → 662,12
btn.setEnabled(ok);
}
}
 
private boolean isRO() {
final SQLRowAccessor r = getListe().getSelectedRow();
return r != null && SQLComponent.isReadOnly(r);
}
}
 
protected final void updateOrderButtons() {
this.btnMngr.updateBtn(this.buttonMoins);
681,7 → 711,8
public void setUpAndDownVisible(boolean b) {
this.buttonPlus.setVisible(b);
this.buttonMoins.setVisible(b);
 
// also disable move by drag and drop
this.getListe().getJTable().setDragEnabled(b);
}
 
public void setAddVisible(boolean b) {
716,11 → 747,19
this.buttonClone.setVisible(b);
}
 
public void setReadWriteButtonsVisible(final boolean b) {
this.setUpAndDownVisible(b);
this.setAddVisible(b);
this.setDeleteVisible(b);
this.setModifyVisible(b);
this.setCloneVisible(b);
}
 
public void setSearchFullMode(boolean b) {
if (b)
this.searchPanel.setBorder(BorderFactory.createEtchedBorder());
else
this.searchPanel.setBorder(null);
this.searchPanel.setBorder(BorderFactory.createEmptyBorder());
this.searchComponent.setSearchFullMode(b);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/PropsConfiguration.java
38,6 → 38,7
import org.openconcerto.utils.NetUtils;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.StreamUtils;
import org.openconcerto.utils.Value;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.i18n.TranslationManager;
 
117,6 → 118,11
*/
public static final String REDIRECT_TO_FILE = "redirectToFile";
 
// properties cannot contain null, so to be able to override a default, a non-null value
// meaning empty must be chosen (as setProperty(name, null) is the same as remove(name) i.e.
// get the value from the default properties)
public static final String EMPTY_PROP_VALUE;
 
protected static enum FileMode {
IN_JAR, NORMAL_FILE
};
152,6 → 158,9
DEFAULTS.setProperty("customer", "test");
DEFAULTS.setProperty("server.ip", "127.0.0.1");
DEFAULTS.setProperty("server.login", "root");
 
EMPTY_PROP_VALUE = "";
assert EMPTY_PROP_VALUE != null;
}
 
private final Properties props;
313,8 → 322,9
}
 
protected final DBRoot createRoot() {
if (getRootName() != null)
return this.getSystemRoot().getRoot(getRootName());
final Value<String> rootName = getRootNameValue();
if (rootName.hasValue())
return this.getSystemRoot().getRoot(rootName.getValue());
else
throw new NullPointerException("no rootname");
}
325,9 → 335,14
}
 
public String getRootName() {
return this.getProperty("base.root");
return this.getProperty("base.root", EMPTY_PROP_VALUE);
}
 
public final Value<String> getRootNameValue() {
final String res = getRootName();
return res == null || EMPTY_PROP_VALUE.equals(res) ? Value.<String> getNone() : Value.getSome(res);
}
 
protected SQLFilter createFilter() {
return SQLFilter.create(this.getSystemRoot(), getDirectory());
}
493,8 → 508,9
protected Collection<String> getRootsToMap() {
final Set<String> res = new HashSet<String>();
 
if (this.getRootName() != null)
res.add(this.getRootName());
final Value<String> rootName = getRootNameValue();
if (rootName.hasValue())
res.add(rootName.getValue());
final String rootsToMap = getProperty("systemRoot.rootsToMap");
if (rootsToMap != null)
res.addAll(SQLRow.toList(rootsToMap));
992,7 → 1008,7
return fieldMapper;
}
 
public void setFieldMapper(FieldMapper fieldMapper) {
protected void setFieldMapper(FieldMapper fieldMapper) {
this.fieldMapper = fieldMapper;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/ConnexionPanel.java
52,6 → 52,7
import java.util.Locale;
import java.util.ResourceBundle;
 
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
333,7 → 334,7
this.langButton.setOpaque(false);
this.langButton.setBorderPainted(false);
this.langButton.setContentAreaFilled(false);
this.langButton.setBorder(null);
this.langButton.setBorder(BorderFactory.createEmptyBorder());
this.langButton.setFocusable(false);
this.langButton.setVisible(false);
panelButton.add(this.langButton, c2);
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/SQLTextCombo.java
20,15 → 20,18
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLRowItemView;
import org.openconcerto.sql.sqlobject.itemview.RowItemViewComponent;
import org.openconcerto.sql.utils.SQLCreateMoveableTable;
import org.openconcerto.ui.component.ComboLockedMode;
import org.openconcerto.ui.component.ITextComboCache;
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
40,6 → 43,26
*/
public class SQLTextCombo extends org.openconcerto.ui.component.ITextCombo implements RowItemViewComponent {
 
static public final String getTableName() {
return "COMPLETION";
}
 
static public final String getRefFieldName() {
return "CHAMP";
}
 
static public final String getValueFieldName() {
return "LABEL";
}
 
static public final SQLCreateMoveableTable getCreateTable(SQLSyntax syntax) {
final SQLCreateMoveableTable createTable = new SQLCreateMoveableTable(syntax, getTableName());
createTable.addVarCharColumn(getRefFieldName(), 100);
createTable.addVarCharColumn(getValueFieldName(), 200);
createTable.setPrimaryKey(getRefFieldName(), getValueFieldName());
return createTable;
}
 
public SQLTextCombo() {
super();
}
74,7 → 97,7
 
public ITextComboCacheSQL(final DBRoot r, final String id) {
this.field = id;
this.t = r.findTable("COMPLETION");
this.t = r.findTable(getTableName());
if (!this.isValid())
Log.get().warning("no completion found for " + this.field);
this.cache = new ArrayList<String>();
90,9 → 113,11
}
 
public List<String> loadCache(final boolean dsCache) {
final SQLSelect sel = new SQLSelect(this.t.getBase());
sel.addSelect(this.t.getField("LABEL"));
sel.setWhere(new Where(this.t.getField("CHAMP"), "=", this.field));
final SQLSelect sel = new SQLSelect();
sel.addSelect(this.t.getField(getValueFieldName()));
sel.setWhere(new Where(this.t.getField(getRefFieldName()), "=", this.field));
// predictable order
sel.addFieldOrder(this.t.getField(getValueFieldName()));
// ignore DS cache to allow the fetching of rows modified by another VM
@SuppressWarnings("unchecked")
final List<String> items = (List<String>) this.getDS().execute(sel.asString(), new IResultSetHandler(SQLDataSource.COLUMN_LIST_HANDLER) {
123,21 → 148,24
public void addToCache(String string) {
if (!this.cache.contains(string)) {
final Map<String, Object> m = new HashMap<String, Object>();
m.put("CHAMP", this.field);
m.put("LABEL", string);
m.put(getRefFieldName(), this.field);
m.put(getValueFieldName(), string);
try {
// the primary key is not generated so don't let SQLRowValues remove it.
new SQLRowValues(this.t, m).insert(true, false);
} catch (SQLException e) {
// e.g. some other VM hasn't already added it
e.printStackTrace();
}
// add anyway since we didn't contain it
this.cache.add(string);
}
}
 
public void deleteFromCache(String string) {
final Where w = new Where(this.t.getField("CHAMP"), "=", this.field).and(new Where(this.t.getField("LABEL"), "=", string));
final Where w = new Where(this.t.getField(getRefFieldName()), "=", this.field).and(new Where(this.t.getField(getValueFieldName()), "=", string));
this.getDS().executeScalar("DELETE FROM " + this.t.getSQLName().quote() + " WHERE " + w.getClause());
this.cache.removeAll(Collections.singleton(string));
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/IComboModel.java
105,7 → 105,8
 
this.search = null;
this.runnables = new ArrayList<Runnable>();
this.setWillUpdate(null);
this.willUpdate = null;
this.updating = false;
this.itemsByID = new HashMap<Integer, IComboSelectionItem>();
this.addMissingItem = true;
 
155,6 → 156,8
}
};
 
// ATTN this listener is notified last (see fireContentsChanged()) but this OK as it merely
// fire and doesn't update our state
this.addListDataListener(new ListDataListener() {
@Override
public void intervalRemoved(ListDataEvent e) {
168,16 → 171,26
 
@Override
public void contentsChanged(ListDataEvent e) {
if (e.getIndex0() == -1 && e.getIndex1() == -1) {
// selection change
comboValueChanged();
} else {
if (e.getIndex0() != -1 || e.getIndex1() != -1) {
itemsChanged();
} // else selection change
}
}
});
}
 
@Override
protected void fireContentsChanged(Object source, int index0, int index1) {
// Our superclass notifies the listeners in reverse order, i.e. our listener is notified
// last. Thus if a listener access getWantedID() it will get the previous value. To avoid
// that we overload the fire() to change our state before notifying other listeners.
if (index0 == -1 && index1 == -1) {
// selection change
comboValueChanged();
}
 
super.fireContentsChanged(source, index0, index1);
}
 
void setRunning(final boolean b) {
if (this.running != b) {
this.running = b;
522,9 → 535,10
}
 
private final void comboValueChanged() {
this.propSupp.firePropertyChange("selectedValue", null, getSelectedValue());
// update our state before firing
if (!this.isUpdating())
this.setWantedID(this.getSelectedId());
this.propSupp.firePropertyChange("selectedValue", null, getSelectedValue());
}
 
private void selectID(int id) {
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/SQLRequestComboBox.java
15,10 → 15,13
 
import org.openconcerto.sql.element.RIVPanel;
import org.openconcerto.sql.element.SQLComponentItem;
import org.openconcerto.sql.model.SQLImmutableRowValues;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.request.SQLForeignRowItemView;
import org.openconcerto.sql.request.SQLRowItemView;
import org.openconcerto.sql.view.search.SearchSpec;
25,6 → 28,7
import org.openconcerto.ui.FontUtils;
import org.openconcerto.ui.component.ComboLockedMode;
import org.openconcerto.ui.component.combo.ISearchableCombo;
import org.openconcerto.ui.component.combo.SearchMode;
import org.openconcerto.ui.component.text.TextComponent;
import org.openconcerto.ui.coreanimation.Pulseable;
import org.openconcerto.ui.valuewrapper.ValueChangeSupport;
175,6 → 179,10
return this.req != null;
}
 
public final IComboModel getModel() {
return this.req;
}
 
public final void uiInit(final IComboModel req) {
if (hasModel())
throw new IllegalStateException(this + " already inited.");
420,6 → 428,24
}
}
 
/**
* The currently selected row in the UI. The result depends on the
* {@link ComboSQLRequest#keepRows(KeepMode) keep mode} of the {@link #getRequest() request}.
*
* @return the currently selected row, <code>null</code> if none or for {@link KeepMode#NONE}.
*/
public final SQLRowAccessor getSelectedRowAccessor() {
final IComboSelectionItem selectedValue = this.req.getSelectedValue();
if (selectedValue == null)
return null;
 
final SQLRowAccessor res = selectedValue.getRow();
if (res == null || res instanceof SQLRow)
return res;
else
return new SQLImmutableRowValues((SQLRowValues) res);
}
 
private void modelValueChanged() {
final IComboSelectionItem newValue = this.req.getSelectedValue();
// user makes invalid edit => combo invalid=true and value=null => model value=null
550,9 → 576,9
} else {
final int flag = value.getFlag();
if (flag == IComboSelectionItem.WARNING_FLAG)
i = new ImageIcon(this.getClass().getResource("warning.png"));
i = new ImageIcon(SQLRequestComboBox.class.getResource("warning.png"));
else if (flag == IComboSelectionItem.ERROR_FLAG)
i = new ImageIcon(this.getClass().getResource("error.png"));
i = new ImageIcon(SQLRequestComboBox.class.getResource("error.png"));
else
i = null;
}
581,4 → 607,10
public final boolean isUpdating() {
return this.req.isUpdating();
}
 
// completion
public final void setCompletionMode(SearchMode m) {
this.combo.setCompletionMode(m);
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/Configuration.java
22,6 → 22,7
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBStructureItem;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.FieldMapper;
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLFilter;
import org.openconcerto.sql.model.SQLTable;
74,6 → 75,8
 
public abstract SQLElementDirectory getDirectory();
 
public abstract FieldMapper getFieldMapper();
 
public abstract File getWD();
 
// abstract :
/trunk/OpenConcerto/src/org/openconcerto/sql/request/BaseFillSQLRequest.java
168,7 → 168,7
}
 
protected List<Path> getOrder() {
return Collections.singletonList(new Path(getPrimaryTable()));
return Collections.singletonList(Path.get(getPrimaryTable()));
}
 
public final void setWhere(final Where w) {
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLFieldTranslator.java
33,6 → 33,7
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.io.FileInputStream;
65,9 → 66,18
public class SQLFieldTranslator {
 
// OK since RowItemDesc is immutable
/**
* Instance representing "no description".
*/
public static final RowItemDesc NULL_DESC = new RowItemDesc(null, null);
 
private static final String METADATA_TABLENAME = SQLSchema.FWK_TABLENAME_PREFIX + "RIV_METADATA";
 
/**
* Use the code and not the table name, since the same table might be used differently at
* different times (e.g. dropped then recreated some time later with a different purpose). Or
* conversely, a table might get renamed.
*/
private static final String ELEM_FIELDNAME = "ELEMENT_CODE";
private static final String COMP_FIELDNAME = "COMPONENT_CODE";
private static final String ITEM_FIELDNAME = "ITEM";
207,41 → 217,63
this.load(b, CORE_VARIANT, inputStream);
}
 
public Set<SQLTable> load(DBRoot b, final String variant, InputStream inputStream) {
/**
* Load translations from the passed stream.
*
* @param b the default root for tables.
* @param variant the variant to use.
* @param inputStream the XML.
* @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) {
if (inputStream == null)
throw new NullPointerException("inputStream is null");
final Set<SQLTable> res = new HashSet<SQLTable>();
final Set<String> notFound = new HashSet<String>();
try {
final Document doc = new SAXBuilder().build(inputStream);
// System.out.println("Base de donnée:"+base);
for (final Element elem : getChildren(doc.getRootElement())) {
final String elemName = elem.getName().toLowerCase();
final DBRoot root;
final List<Element> tableElems;
if (elemName.equals("table")) {
res.add(load(b, variant, elem));
root = b;
tableElems = Collections.singletonList(elem);
} else if (elemName.equals("root")) {
final DBRoot root = b.getDBSystemRoot().getRoot(elem.getAttributeValue("name"));
for (final Element tableElem : getChildren(elem)) {
res.add(load(root, variant, tableElem));
root = b.getDBSystemRoot().getRoot(elem.getAttributeValue("name"));
tableElems = getChildren(elem);
} else {
root = null;
tableElems = null;
}
if (tableElems != null) {
for (final Element tableElem : tableElems) {
final Tuple2<String, SQLTable> t = load(root, variant, tableElem, true);
if (t.get1() == null) {
notFound.add(t.get0());
} else {
res.add(t.get1());
}
}
}
}
// load() returns null if no table is found
assert !res.contains(null);
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return res;
return Tuple2.create(res, notFound);
}
 
private SQLTable load(DBRoot b, final String variant, final Element tableElem) {
private Tuple2<String, SQLTable> load(DBRoot b, final String variant, final Element tableElem, final boolean lenient) {
final String tableName = tableElem.getAttributeValue("name");
SQLTable table = b.getTable(tableName);
if (table == null && this.dir != null && this.dir.getElement(tableName) != null)
table = this.dir.getElement(tableName).getTable();
if (table == null) {
// 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);
} else {
if (table != null) {
for (final Element elem : getChildren(tableElem)) {
final String elemName = elem.getName().toLowerCase();
if (elemName.equals("field")) {
253,8 → 285,13
}
}
}
} 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);
} else {
throw new IllegalStateException("Table not found : " + tableName);
}
return table;
return Tuple2.create(tableName, table);
}
 
private void load(final SQLTable table, final String compCode, final String variant, final Element fieldElem) {
377,6 → 414,17
return getDescFor(t, SQLElement.DEFAULT_COMP_ID, name);
}
 
/**
* Find the description for the passed item. This method will search for an SQLElement for
* <code>t</code> to get its {@link SQLElement#getMDPath() variants path}.
*
* @param t the table.
* @param compCode the component code.
* @param name the item name.
* @return the first description that matches the parameters, never <code>null</code> but
* {@link #NULL_DESC}.
* @see #getDescFor(SQLTable, String, List, String)
*/
public RowItemDesc getDescFor(SQLTable t, String compCode, String name) {
final SQLElement element = this.dir == null ? null : this.dir.getElement(t);
final List<String> variants = element == null ? Collections.<String> emptyList() : element.getMDPath();
394,6 → 442,20
return this.getDescFor(elem.getTable(), compCode, variants, name);
}
 
/**
* Find the description for the passed item. This method will try {compCode, variant, item}
* first with the DB variant, then with each passed variant and last with the default variant,
* until one description is found. If none is found, it will retry with the code
* {@link SQLElement#DEFAULT_COMP_ID}. If none is found, it will retry all the above after
* having refreshed its cache from the DB.
*
* @param t the table.
* @param compCodeArg the component code.
* @param variants the variants to search, not <code>null</code> but can be empty.
* @param name the item name.
* @return the first description that matches the parameters, never <code>null</code> but
* {@link #NULL_DESC}.
*/
public RowItemDesc getDescFor(SQLTable t, String compCodeArg, List<String> variants, String name) {
RowItemDesc labeledField = this.getTranslation(t, compCodeArg, variants, name);
// if nothing found, re-fetch from the DB
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLRowView.java
14,6 → 14,7
package org.openconcerto.sql.request;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
31,6 → 32,9
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
57,7 → 61,9
private int selectedID;
// les valeurs affichées
private final Map<String, SQLRowItemView> views;
private final List<SQLRowItemView> viewsOrdered;
 
private boolean readOnly;
private boolean filling;
private boolean updating;
 
73,6 → 79,8
this.supp = new PropertyChangeSupport(this);
this.table = t;
this.views = new HashMap<String, SQLRowItemView>();
this.viewsOrdered = new LinkedList<SQLRowItemView>();
this.readOnly = false;
this.filling = false;
this.updating = false;
this.selectedID = SQLRow.NONEXISTANT_ID;
115,6 → 123,23
};
}
 
private synchronized void setReadOnlySelection(final boolean b) {
if (this.readOnly != b) {
this.readOnly = b;
this.supp.firePropertyChange(SQLComponent.READ_ONLY_PROP, !b, b);
}
}
 
// false if no ID
public synchronized boolean isReadOnlySelection() {
return this.readOnly;
}
 
private final void checkRO() throws SQLException {
if (this.isReadOnlySelection())
throw new SQLException("Read only selection");
}
 
public synchronized boolean isUpdating() {
return this.updating;
}
140,9 → 165,30
if (this.views.containsKey(obj.getSQLName()))
throw new IllegalStateException("2 views named " + obj.getSQLName() + ": " + this.views.get(obj.getSQLName()) + " " + obj);
this.views.put(obj.getSQLName(), obj);
this.viewsOrdered.add(obj);
assert this.viewsOrdered.size() == this.views.size();
}
 
/**
* Set the order of the views. Useful when some views depend on others.
*
* @param names names of the {@link SQLRowItemView}.
*/
public final void setViewsOrder(final Collection<String> names) {
final LinkedHashSet<String> nameSet = new LinkedHashSet<String>(names);
if (!this.views.keySet().equals(nameSet))
throw new IllegalArgumentException("Names mismatch " + this.views.keySet() + " != " + nameSet);
 
final LinkedList<SQLRowItemView> l = new LinkedList<SQLRowItemView>();
for (final String n : nameSet) {
l.add(this.views.get(n));
}
this.viewsOrdered.clear();
this.viewsOrdered.addAll(l);
assert this.viewsOrdered.size() == this.views.size();
}
 
/**
* Display the passed id. As an exception, this can be called from outside the EDT
*
* @param id id of the row to display.
156,18 → 202,24
});
}
 
public void select(SQLRowAccessor r) {
this.select(r, null);
}
 
/**
* Fill the item views with values from <code>r</code>. If r is <code>null</code> this will be
* reset. If r has no ID, the selectedID doesn't change.
*
* @param r the row to display, can be <code>null</code>.
* @param views the subset of views to fill, <code>null</code> meaning all.
*/
public void select(SQLRowAccessor r) {
public void select(final SQLRowAccessor r, final Set<String> views) {
this.setFilling(true);
try {
if (r == null) {
this.setSelectedID(SQLRow.NONEXISTANT_ID);
for (final SQLRowItemView view : this.getViewsFast()) {
if (views == null || views.contains(view.getSQLName()))
view.resetValue();
}
} else {
177,9 → 229,25
if (r.getID() != SQLRow.NONEXISTANT_ID)
this.setSelectedID(r.getID());
for (final SQLRowItemView view : this.getViewsFast()) {
if (views == null || views.contains(view.getSQLName()))
view.show(r);
}
}
 
if (this.getTable().contains(SQLComponent.READ_ONLY_FIELD)) {
final Boolean readOnlySel;
if (r == null) {
readOnlySel = false;
} else if (r.getIDNumber() == null) {
// don't change the row => same value
readOnlySel = null;
} else {
readOnlySel = SQLComponent.isReadOnly(r);
}
if (readOnlySel != null) {
this.setReadOnlySelection(readOnlySel);
}
}
} finally {
this.setFilling(false);
}
187,6 → 255,7
 
public final void detach() {
this.setSelectedID(SQLRow.NONEXISTANT_ID);
this.setReadOnlySelection(false);
}
 
/**
195,6 → 264,7
* @throws SQLException if the update couldn't complete.
*/
public void update() throws SQLException {
checkRO();
// this ship is sailed, don't accept updates from the db anymore
// this allows to archive a private (thus changing our fk) without
// overwriting our values
231,7 → 301,8
return fillVals().insert();
}
 
private SQLRowValues fillVals() {
private SQLRowValues fillVals() throws SQLException {
checkRO();
final SQLRowValues vals = new SQLRowValues(this.getTable());
for (final SQLRowItemView view : this.getViewsFast()) {
view.insert(vals);
250,11 → 321,11
}
 
public Set<SQLRowItemView> getViews() {
return new HashSet<SQLRowItemView>(this.getViewsFast());
return new LinkedHashSet<SQLRowItemView>(this.getViewsFast());
}
 
private final Collection<SQLRowItemView> getViewsFast() {
return this.views.values();
return this.viewsOrdered;
}
 
public String toString() {
278,7 → 349,7
* @return the corresponding view, or <code>null</code> if none exists.
*/
public SQLRowItemView getView(Component comp) {
for (final SQLRowItemView view : this.views.values()) {
for (final SQLRowItemView view : this.getViewsFast()) {
if (SwingUtilities.isDescendingFrom(comp, view.getComp()))
return view;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/RowItemDesc.java
50,4 → 50,9
public String getDocumentation() {
return this.documentation;
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + " " + (this.getLabel() == null ? "<empty>" : this.getLabel());
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ComboSQLRequest.java
20,6 → 20,7
import org.openconcerto.sql.TransfFieldExpander;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
28,6 → 29,7
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple3;
39,6 → 41,7
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
 
// final: use setSelectTransf()
53,8 → 56,24
}
};
 
public static enum KeepMode {
/**
* Only the ID is kept.
*/
NONE,
/**
* Only the {@link SQLRow} is kept.
*/
ROW,
/**
* The full {@link SQLRowValues graph} is kept.
*/
GRAPH
}
 
private static final String SEP_CHILD = " ◄ ";
private static String SEP_FIELD;
private static Comparator<? super IComboSelectionItem> DEFAULT_COMPARATOR;
 
/**
* Set the default {@link #setFieldSeparator(String) field separator}.
65,8 → 84,13
SEP_FIELD = separator;
}
 
public static void setDefaultItemsOrder(final Comparator<? super IComboSelectionItem> comp) {
DEFAULT_COMPARATOR = comp;
}
 
static {
setDefaultFieldSeparator(" | ");
setDefaultItemsOrder(null);
}
 
// immutable
75,10 → 99,11
 
private String fieldSeparator = SEP_FIELD;
private String undefLabel;
private boolean keepRows;
private KeepMode keepRows;
private IClosure<IComboSelectionItem> customizeItem;
 
private List<Path> order;
private Comparator<? super IComboSelectionItem> itemsOrder;
 
public ComboSQLRequest(SQLTable table, List<String> l) {
this(table, l, null);
88,9 → 113,10
super(table, where);
this.undefLabel = null;
// don't use memory
this.keepRows = false;
this.keepRows = KeepMode.NONE;
this.customizeItem = null;
this.order = null;
this.itemsOrder = DEFAULT_COMPARATOR;
this.exp = new TransfFieldExpander(new ITransformer<SQLField, List<SQLField>>() {
@Override
public List<SQLField> transformChecked(SQLField fk) {
193,6 → 219,8
throw new RTInterruptedException("interrupted in fill");
result.add(createItem(vals));
}
if (this.itemsOrder != null)
Collections.sort(result, this.itemsOrder);
 
cache.put(cacheKey, result, this.getTables());
 
233,6 → 261,24
this.clearGraph();
}
 
public final void setNaturalItemsOrder(final boolean b) {
this.setItemsOrder(b ? CompareUtils.<IComboSelectionItem> naturalOrder() : null);
}
 
/**
* Set the in-memory sort on items.
*
* @param comp how to sort items, <code>null</code> meaning don't sort (i.e. only
* {@link #getOrder() SQL order} will be used).
*/
public final void setItemsOrder(final Comparator<? super IComboSelectionItem> comp) {
this.itemsOrder = comp;
}
 
public final Comparator<? super IComboSelectionItem> getItemsOrder() {
return this.itemsOrder;
}
 
private final IComboSelectionItem createItem(final SQLRowValues rs) {
final String desc;
if (this.undefLabel != null && rs.getID() == getPrimaryTable().getUndefinedID())
250,8 → 296,13
return CollectionUtils.join(filtered, ComboSQLRequest.this.fieldSeparator);
}
});
// don't store the whole SQLRowValues to save some memory
final IComboSelectionItem res = this.keepRows ? new IComboSelectionItem(rs.asRow(), desc) : new IComboSelectionItem(rs.getID(), desc);
final IComboSelectionItem res;
if (this.keepRows == KeepMode.GRAPH)
res = new IComboSelectionItem(rs, desc);
else if (this.keepRows == KeepMode.ROW)
res = new IComboSelectionItem(rs.asRow(), desc);
else
res = new IComboSelectionItem(rs.getID(), desc);
if (this.customizeItem != null)
this.customizeItem.executeChecked(res);
return res;
309,6 → 360,10
* @see IComboSelectionItem#getRow()
*/
public final void keepRows(boolean b) {
this.keepRows = b;
this.keepRows(b ? KeepMode.ROW : KeepMode.NONE);
}
 
public final void keepRows(final KeepMode mode) {
this.keepRows = mode;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/UpdateBuilder.java
16,6 → 16,7
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSyntax;
66,6 → 67,11
throw new IllegalArgumentException("unknown " + field + " in " + this.getTable().getSQLName());
}
 
private final void checkField(final SQLField field) {
if (this.getTable() != field.getTable())
throw new IllegalArgumentException(field + " not in " + this.getTable().getSQLName());
}
 
public final UpdateBuilder set(final String field, final String value) {
this.checkField(field);
this.fields.put(field, value);
72,6 → 78,17
return this;
}
 
public final UpdateBuilder setObject(final String fieldName, final Object value) {
this.fields.put(fieldName, getTable().getField(fieldName).getType().toString(value));
return this;
}
 
public final UpdateBuilder setObject(final SQLField field, final Object value) {
this.checkField(field);
this.fields.put(field.getName(), field.getType().toString(value));
return this;
}
 
private final boolean isJoinVirtual(final String alias) {
if (!this.virtualJoins.containsKey(alias))
throw new IllegalArgumentException("Not a join " + alias);
/trunk/OpenConcerto/src/org/openconcerto/sql/request/Inserter.java
New file
0,0 → 1,203
/*
* 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.request;
 
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.utils.SQLCreateTable;
import org.openconcerto.sql.utils.SQLCreateTableBase;
import org.openconcerto.sql.utils.SQLUtils;
 
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
 
/**
* Allow to insert some rows into a table and get some feedback.
*
* @author Sylvain
*/
public class Inserter {
 
public static enum ReturnMode {
NO_FIELDS, FIRST_FIELD, ALL_FIELDS;
}
 
public static final class Insertion<T> {
private final List<T> list;
private final int count;
 
public Insertion(List<T> res, int count) {
super();
this.list = res;
this.count = count;
assert res == null || res.size() <= count;
}
 
/**
* The number of rows inserted.
*
* @return number of rows inserted.
*/
public final int getCount() {
return this.count;
}
 
/**
* The list of inserted rows. Can be <code>null</code> if it couldn't be retrieved, e.g.
* MySQL only supports single primary key. Likewise the size can be less than
* {@link #getCount()}, e.g. {@link SQLSystem#H2} only support one.
*
* @return the list of inserted rows, or <code>null</code>.
*/
public final List<T> getRows() {
return this.list;
}
}
 
private static final String[] EMPTY_ARRAY = new String[0];
 
private final List<String> pk;
private final DBSystemRoot sysRoot;
private final SQLName tableName;
 
public Inserter(final SQLTable t) {
this(t.getDBSystemRoot(), t.getSQLName(), t.getPKsNames());
}
 
public Inserter(final SQLCreateTable t) {
this(t.getRoot().getDBSystemRoot(), new SQLName(t.getRootName(), t.getName()), t.getPrimaryKey());
}
 
public Inserter(final SQLCreateTableBase<?> t, final DBRoot r) {
this(r.getDBSystemRoot(), new SQLName(r.getName(), t.getName()), t.getPrimaryKey());
}
 
public Inserter(final DBSystemRoot sysRoot, final SQLName tableName, final List<String> pk) {
super();
if (sysRoot == null || tableName == null)
throw new NullPointerException();
this.sysRoot = sysRoot;
this.tableName = tableName;
this.pk = pk;
}
 
protected final SQLSystem getSystem() {
return this.sysRoot.getServer().getSQLSystem();
}
 
public final Insertion<?> insertReturnFirstField(final String sql) throws SQLException {
return insertReturnFirstField(sql, true);
}
 
public final Insertion<?> insertReturnFirstField(final String sql, final boolean requireAllRows) throws SQLException {
return insert(sql, ReturnMode.FIRST_FIELD, requireAllRows);
}
 
/**
* Insert rows with the passed SQL.
*
* @param sql the SQL specifying the data to be inserted, e.g. ("DESIGNATION") VALUES('A'),
* ('B').
* @return an object to always know the insertion count and possibly the inserted primary keys.
* @throws SQLException if an error occurs while inserting.
*/
@SuppressWarnings("unchecked")
public final Insertion<Object[]> insertReturnAllFields(final String sql) throws SQLException {
return (Insertion<Object[]>) insert(sql, ReturnMode.ALL_FIELDS, true);
}
 
/**
* Insert rows with the passed SQL. Should be faster than other insert methods since it doesn't
* fetch primary keys.
*
* @param sql the SQL specifying the data to be inserted, e.g. ("DESIGNATION") VALUES('A'),
* ('B').
* @return the insertion count.
* @throws SQLException if an error occurs while inserting.
*/
public final int insertCount(final String sql) throws SQLException {
return insert(sql, ReturnMode.NO_FIELDS, false).getCount();
}
 
/**
* Insert rows with the passed SQL.
* <p>
* NOTE: only some systems support returning all rows.
* </p>
*
* @param sql the SQL specifying the data to be inserted, e.g. ("DESIGNATION") VALUES('A'),
* ('B').
* @param mode which fields should be returned.
* @param requireAllRows <code>true</code> if there must be a returned row for each inserted
* row, only meaningful for <code>mode != {@link ReturnMode#NO_FIELDS}</code>.
* @return an object to always know the insertion count and possibly the inserted primary keys.
* @throws SQLException if an error occurs while inserting.
*/
public final Insertion<?> insert(final String sql, final ReturnMode mode, final boolean requireAllRows) throws SQLException {
final SQLSystem sys = getSystem();
 
// MAYBE instead of throwing exception :
// 0. check that PK type is serial
// 1. connection.setTransactionIsolation(TRANSACTION_SERIALIZABLE)
// 2. previousMax = select max(PK)
// 3. insert
// 4. select PK where PK > previousMax
final boolean requireAllFields = mode != ReturnMode.NO_FIELDS && requireAllRows;
if (requireAllFields && sys == SQLSystem.H2)
throw new IllegalArgumentException("H2 use IDENTITY() which only returns the last ID: " + this.tableName);
if (requireAllFields && sys == SQLSystem.MSSQL)
throw new IllegalArgumentException("In MS getUpdateCount() is correct but getGeneratedKeys() only returns the last ID: " + this.tableName);
 
return SQLUtils.executeAtomic(this.sysRoot.getDataSource(), new ConnectionHandlerNoSetup<Insertion<?>, SQLException>() {
@Override
public Insertion<?> handle(SQLDataSource ds) throws SQLException {
final Statement stmt = ds.getConnection().createStatement();
try {
// ATTN don't call quote() with the passed sql otherwise it will try to parse %
final String insertInto = "INSERT INTO " + Inserter.this.tableName.quote() + " " + sql;
final int count;
// MySQL always return an empty resultSet for anything else than 1 pk
final boolean dontGetGK = mode == ReturnMode.NO_FIELDS || Inserter.this.pk.size() == 0 || (sys == SQLSystem.MYSQL && Inserter.this.pk.size() != 1);
if (sys == SQLSystem.POSTGRESQL) {
// in psql Statement.RETURN_GENERATED_KEYS actually return all fields
count = stmt.executeUpdate(insertInto, dontGetGK ? EMPTY_ARRAY : Inserter.this.pk.toArray(EMPTY_ARRAY));
} else {
count = stmt.executeUpdate(insertInto, dontGetGK ? Statement.NO_GENERATED_KEYS : Statement.RETURN_GENERATED_KEYS);
}
final List<?> list;
// cannot get or doesn't want the list
if (dontGetGK) {
list = null;
} else {
list = (List<?>) (mode == ReturnMode.FIRST_FIELD ? SQLDataSource.COLUMN_LIST_HANDLER : SQLDataSource.ARRAY_LIST_HANDLER).handle(stmt.getGeneratedKeys());
assert list.size() <= count;
if (requireAllRows && list.size() != count)
throw new IllegalStateException("Missing keys");
}
@SuppressWarnings("unchecked")
final Insertion<?> res = new Insertion<Object>((List<Object>) list, count);
return res;
} finally {
stmt.close();
}
}
});
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/FilteredFillSQLRequest.java
117,7 → 117,7
if (w == null || p == null) {
this.filterInfo = Tuple2.create(null, null);
} else {
this.filterInfo = Tuple2.create((Set<SQLRow>) new HashSet<SQLRow>(w), new Path(p));
this.filterInfo = Tuple2.create((Set<SQLRow>) new HashSet<SQLRow>(w), p);
}
fireWhereChange();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ListSQLRequest.java
15,6 → 15,7
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.FieldExpander;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
107,13 → 108,16
addField(graphToFetch, getPrimaryTable().getCreationUserField());
addField(graphToFetch, getPrimaryTable().getModifDateField());
addField(graphToFetch, getPrimaryTable().getModifUserField());
addField(graphToFetch, getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_FIELD));
addField(graphToFetch, getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_USER_FIELD));
}
 
private void addField(SQLRowValues graphToFetch, final SQLField f) {
if (f != null)
if (f != null && !graphToFetch.getFields().contains(f.getName())) {
if (f.isKey())
graphToFetch.put(f.getName(), new SQLRowValues(f.getTable().getForeignTable(f.getName())).put("NOM", null).put("PRENOM", null));
graphToFetch.putRowValues(f.getName()).putNulls("NOM", "PRENOM");
else
graphToFetch.put(f.getName(), null);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/BaseSQLComponent.java
66,15 → 66,19
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
 
112,6 → 116,8
}
 
private final SQLRowView requete;
// whether the application (as opposed to us, the framework) allow the edition
private final Map<String, Boolean> allowEditable;
 
private final Set<SQLRowItemView> required;
// contains the SQL name of required SQLRowItemView
124,6 → 130,7
private boolean alwaysEditable;
private final Set<SQLField> hide;
private FormLayouter additionalFieldsPanel;
private boolean displayFieldName;
 
public BaseSQLComponent(SQLElement element) {
super(element);
144,13 → 151,23
this.editable = true;
this.setNonExistantEditable(false);
this.requete = new SQLRowView(this.getTable());
// enable or disable our views and forward event
this.requete.addListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateChildrenEditable();
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
}, SQLComponent.READ_ONLY_PROP);
this.allowEditable = new HashMap<String, Boolean>();
this.displayFieldName = false;
}
 
private final SQLRowView getRequest() {
return this.requete;
}
 
private SQLField getField(String field) {
protected final SQLField getField(String field) {
return this.getTable().getField(field);
}
 
408,7 → 425,7
this.addView(comp, e.getKey(), spec);
}
// assure that added views are consistent with our editable status
this.setChildrenEditable(this.isEditable());
this.updateChildrenEditable();
for (final SQLRowItemView v : this.getRequest().getViews()) {
v.addEmptyListener(new EmptyListener() {
public void emptyChange(EmptyObj src, boolean newValue) {
456,6 → 473,10
this.additionalFieldsPanel = panel;
}
 
public final void setViewsOrder(final Collection<String> names) {
this.getRequest().setViewsOrder(names);
}
 
public final SQLRowItemView getView(String name) {
return this.getRequest().getView(name);
}
527,43 → 548,91
// not public since desc could be different from getRIVDesc(itemName)
protected final Tuple2<String, Boolean> getDesc(final String itemName, final RowItemDesc desc) {
final boolean emptyLabel = desc.getLabel() == null || desc.getLabel().trim().length() == 0;
return Tuple2.create(emptyLabel ? itemName : getLabel(itemName, desc), !emptyLabel);
final String l;
if (emptyLabel) {
l = itemName;
} else {
l = getLabel(itemName, desc) + (this.displayFieldName ? " [" + itemName + "]" : "");
}
return Tuple2.create(l, !emptyLabel);
}
 
protected final void toggleDisplayFieldsNames() {
this.displayFieldName = !this.displayFieldName;
this.updateUIAll();
}
 
protected String getLabel(final String itemName, final RowItemDesc desc) {
return desc.getLabel();
}
 
/*
* (non-Javadoc)
/**
* Allow the views of this component to be edited. For this to be editable other framework
* predicates must be <code>true</code> (e.g. {@link #isSelectionReadOnly()}).
*
* @see org.openconcerto.sql.SQLComponent#setEditable(boolean)
* @param b true if the application allow the views to be edited.
*/
@Override
public void setEditable(boolean b) {
if (b != this.editable) {
this.editable = b;
this.setChildrenEditable(b);
this.updateChildrenEditable();
}
}
 
private final void setChildrenEditable(boolean b) {
private final void updateChildrenEditable() {
for (final SQLRowItemView o : this.getRequest().getViews()) {
// already taken care in addInitedView()
if (!this.dontEdit(o))
// a view can only be editable if its parent is too
o.setEditable(this.isEditable() && b);
updateEditable(o);
}
}
 
private final boolean isEditable() {
public final boolean isEditable() {
return this.editable;
}
 
public void allowEditable(final String name, final boolean b) {
final SQLRowItemView view = this.getView(name);
if (view == null)
throw new IllegalArgumentException("No view named " + name);
this.allowEditable(view, b);
}
 
/**
* Allow the passed view to be edited. For the view to be editable other framework predicates
* must be <code>true</code> (e.g. {@link #isEditable()}).
*
* @param view the view.
* @param b true if the application allow the view to be edited.
*/
public void allowEditable(final SQLRowItemView view, final boolean b) {
if (view == null)
throw new NullPointerException();
this.allowEditable.put(view.getSQLName(), b);
updateEditable(view);
}
 
private void updateEditable(final SQLRowItemView o) {
// already taken care in addInitedView()
if (!this.dontEdit(o)) {
// a view can only be editable if its parent is too and if the selected row is
// if nonExistantEditable : always editable, otherwise id must exist
// a view can be disabled for other arbitrary application (non framework) reasons
o.setEditable(this.isEditable() && !this.isSelectionReadOnly() && (this.isNonExistantEditable() || this.getSelectedID() != SQLRow.NONEXISTANT_ID)
&& this.allowEditable.get(o.getSQLName()) != Boolean.FALSE);
}
}
 
// final : overload select() if need be
public final void resetValue() {
this.select(null);
}
 
public final void partialReset() {
final Set<String> names = this.getPartialResetNames();
if (names == null || names.size() > 0)
this.select(null, names);
}
 
public final int insert() {
return this.insert(null);
}
582,11 → 651,17
}
}
 
@Override
public final void select(int id) {
this.select(this.getTable().getRow(id));
}
 
@Override
public void select(SQLRowAccessor r) {
this.select(r, null);
}
 
public void select(SQLRowAccessor r, Set<String> views) {
try {
// allow null to pass, ease some code (eg new ListOfSomething().getTable() even if we
// can't see the table)
594,10 → 669,8
throw new IllegalStateException("forbidden");
// MAYBE prevent repaints of each SQLObject with
// this.setIgnoreRepaint(true) or RepaintManager
this.getRequest().select(r);
// if nonExistantEditable : always editable,
// otherwise id must exist
this.setChildrenEditable(this.isNonExistantEditable() || (r != null && r.getID() != SQLRow.NONEXISTANT_ID));
this.getRequest().select(r, views);
this.updateChildrenEditable();
 
// don't put defaults if non-editable (eg bottom part of ListeModifyPanel)
if (r == null && this.isNonExistantEditable()) {
604,7 → 677,7
// selectDefaults() after select() so that we have the final word
// eg default DESIGNATION of OBS is "ok", but default DESIGNATION of TRANSFO.ID_OBS
// is "good".
this.selectDefaults();
this.selectDefaults(views);
}
} catch (RowNotFound e) {
// l'id demandé n'existe pas : prévenir tout le monde
616,10 → 689,10
}
 
// private since it only changes views having default values : use #resetValue()
private final void selectDefaults() {
private final void selectDefaults(Set<String> views) {
final SQLRowValues defaults = this.createDefaults();
if (defaults != null && defaults.getFields().size() > 0)
this.getRequest().select(defaults);
this.getRequest().select(defaults, views);
}
 
public final void addFillingListener(final PropertyChangeListener l) {
657,6 → 730,11
return this.getRequest().getSelectedID();
}
 
@Override
public final boolean isSelectionReadOnly() {
return this.getRequest().isReadOnlySelection();
}
 
public void update() {
try {
if (!UserRightsManager.getCurrentUserRights().canModify(getTable()))
671,6 → 749,8
try {
if (!UserRightsManager.getCurrentUserRights().canDelete(getTable()))
throw new SQLException("forbidden");
if (this.isSelectionReadOnly())
throw new SQLException("read only");
// MAYBE for performance (avoid searching for references to cut):
// if (this.isPrivate()) {
// ((BaseSQLComponent) this.getSQLParent().getSQLParent()).getSelectedID();
682,6 → 762,16
}
}
 
/**
* The SQL names that are always reset after an insert. This implementation returns an empty set
* : the component is only reset on {@link #getResetMode() visibility change}.
*
* @return the names to reset, <code>null</code> meaning all.
*/
public Set<String> getPartialResetNames() {
return Collections.emptySet();
}
 
// ** required
 
protected final Set<SQLRowItemView> getRequired() {
760,6 → 850,13
label.repaint();
}
 
protected final void updateUIAll() {
for (final SQLRowItemView v : this.getRequest().getViews()) {
final String sqlName = v.getSQLName();
updateUI(sqlName, getRIVDesc(sqlName));
}
}
 
public void doNotShow(SQLField f) {
this.hide.add(f);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/UISQLComponent.java
26,6 → 26,7
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
111,7 → 112,14
return e.isControlDown() ? menu : null;
}
});
added.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.isAltDown())
toggleDisplayFieldsNames();
}
});
}
 
@Override
protected String getLabel(final String itemName, final RowItemDesc desc) {
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLComponent.java
13,11 → 13,15
package org.openconcerto.sql.element;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.SQLKey;
import org.openconcerto.sql.request.SQLRowItemView;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.utils.AlterTable;
import org.openconcerto.utils.cc.ConstantFactory;
import org.openconcerto.utils.cc.IFactory;
import org.openconcerto.utils.checks.ValidObject;
36,6 → 40,33
 
private static final String NONINITED_CODE = new String("default " + SQLComponent.class.getName() + " code");
 
public static final String READ_ONLY_FIELD = "UI_LOCK";
public static final String READ_ONLY_VALUE = "ro";
public static final String READ_WRITE_VALUE = "";
public static final String READ_ONLY_USER_FIELD = SQLKey.PREFIX + "USER_UI_LOCK";
public static final String READ_ONLY_PROP = "readOnlySelection";
 
static public final AlterTable addLockFields(final SQLTable t) {
final AlterTable res = new AlterTable(t);
if (!t.contains(READ_ONLY_FIELD)) {
// writable by default
res.addColumn(READ_ONLY_FIELD, "varchar(16) default " + t.getBase().quoteString(READ_WRITE_VALUE) + " NOT NULL");
res.addForeignColumn(READ_ONLY_USER_FIELD, UserManager.getInstance().getTable());
}
return res;
}
 
static public final boolean isReadOnly(SQLRowAccessor r) {
final SQLRowAccessor roRow;
if (!r.getFields().contains(READ_ONLY_FIELD)) {
Log.get().warning(READ_ONLY_FIELD + " not provided : " + r);
roRow = r.getTable().getRow(r.getID());
} else {
roRow = r;
}
return READ_ONLY_VALUE.equals(roRow.getString(READ_ONLY_FIELD));
}
 
public static enum Mode {
INSERTION, MODIFICATION
}
140,6 → 171,8
 
public abstract int getSelectedID();
 
public abstract boolean isSelectionReadOnly();
 
public abstract void update();
 
public abstract void archive();
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementDirectory.java
136,8 → 136,10
* Adds an already instantiated element.
*
* @param elem the SQLElement to add.
* @return the previously added element.
*/
public synchronized final void addSQLElement(SQLElement elem) {
public synchronized final SQLElement addSQLElement(SQLElement elem) {
final SQLElement res = this.removeSQLElement(elem.getTable());
this.elements.put(elem.getTable(), elem);
this.tableNames.put(elem.getTable().getName(), elem.getTable());
this.byCode.put(elem.getCode(), elem.getTable());
146,6 → 148,7
dl.elementAdded(elem);
}
elem.setDirectory(this);
return res;
}
 
public synchronized final boolean contains(SQLTable t) {
223,6 → 226,7
// MAYBE only reset neighbours.
for (final SQLElement otherElem : this.elements.values())
otherElem.resetRelationships();
elem.setDirectory(null);
for (final DirectoryListener dl : this.listeners) {
dl.elementRemoved(elem);
}
273,7 → 277,7
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.getClass());
final SQLElementNames elementNames = getElementNames(pkgName, TM.getInstance().getTranslationsLocale(), elem.getL18nClass());
return elementNames == null ? null : elementNames.getName(elem);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/element/GroupSQLComponent.java
14,6 → 14,8
package org.openconcerto.sql.element;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.model.FieldMapper;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLType;
import org.openconcerto.sql.request.RowItemDesc;
24,8 → 26,10
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.group.Item;
import org.openconcerto.ui.group.LayoutHints;
import org.openconcerto.utils.i18n.TranslationManager;
 
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
42,7 → 46,10
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
 
52,6 → 59,10
private final int columns = 2;
private final Map<String, JComponent> labels = new HashMap<String, JComponent>();
private final Map<String, JComponent> editors = new HashMap<String, JComponent>();
private String startTabAfter = null;
private boolean tabGroup;
private int tabDepth;
private JTabbedPane pane;
 
public GroupSQLComponent(final SQLElement element, final Group group) {
super(element);
58,16 → 69,23
this.group = group;
}
 
public void startTabGroupAfter(String id) {
startTabAfter = id;
}
 
@Override
protected void addViews() {
this.setLayout(new GridBagLayout());
final GridBagConstraints c = new DefaultGridBagConstraints();
c.fill = GridBagConstraints.NONE;
layout(this.group, 0, 0, 0, c);
this.tabGroup = false;
this.tabDepth = 0;
layout(this.group, 0, 0, 0, c, this);
}
 
public void layout(final Item currentItem, final Integer order, int x, final int level, final GridBagConstraints c) {
public void layout(final Item currentItem, final Integer order, int x, final int level, GridBagConstraints c, JPanel panel) {
final String id = currentItem.getId();
 
final LayoutHints size = currentItem.getLocalHint();
if (!size.isVisible()) {
return;
81,6 → 99,20
if (currentItem instanceof Group) {
final Group currentGroup = (Group) currentItem;
final int stop = currentGroup.getSize();
c.weighty = 0;
if (this.tabGroup && level == this.tabDepth) {
panel = new JPanel();
panel.setLayout(new GridBagLayout());
panel.setOpaque(false);
c = new DefaultGridBagConstraints();
x = 0;
c.fill = GridBagConstraints.NONE;
String label = TranslationManager.getInstance().getTranslationForItem(id);// getRIVDescForId(id).getLabel();
if (label == null) {
label = id;
}
this.pane.addTab(label, panel);
} else {
if (size.showLabel() && getLabel(id) != null) {
x = 0;
c.gridy++;
88,14 → 120,23
c.gridx = 0;
c.weightx = 1;
c.gridwidth = 4;
this.add(getLabel(id), c);
panel.add(getLabel(id), c);
c.gridy++;
}
}
for (int i = 0; i < stop; i++) {
final Item subGroup = currentGroup.getItem(i);
final Integer subGroupOrder = currentGroup.getOrder(i);
layout(subGroup, subGroupOrder, x, level + 1, c);
layout(subGroup, subGroupOrder, x, level + 1, c, panel);
}
if (this.tabGroup && level == this.tabDepth) {
JPanel spacer = new JPanel();
spacer.setOpaque(false);
c.gridy++;
c.weighty = 0.0001;
panel.add(spacer, c);
}
 
} else {
c.gridwidth = 1;
if (size.showLabel()) {
104,11 → 145,12
// Label
if (size.isSeparated()) {
c.gridwidth = 4;
c.weightx = 1;
c.fill = GridBagConstraints.NONE;
} else {
c.fill = GridBagConstraints.HORIZONTAL;
}
this.add(getLabel(id), c);
panel.add(getLabel(id), c);
if (size.isSeparated()) {
c.gridy++;
c.gridx = 0;
150,9 → 192,16
if (c.gridx % 2 == 1) {
c.weightx = 1;
}
this.add(editor, c);
 
panel.add(editor, c);
 
try {
this.addView(editor, id);
JComponent comp = editor;
if (editor instanceof JScrollPane) {
JScrollPane pane = (JScrollPane) editor;
comp = (JComponent) pane.getViewport().getView();
}
this.addView(comp, id);
} catch (final Exception e) {
Log.get().warning(e.getMessage());
}
174,14 → 223,41
}
 
}
if (id.equals(startTabAfter)) {
tabGroup = true;
tabDepth = level;
pane = new JTabbedPane();
c.gridx = 0;
c.gridy++;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
c.gridwidth = 4;
panel.add(pane, c);
}
 
}
 
@Override
public Component addView(JComponent comp, String id) {
final FieldMapper fieldMapper = PropsConfiguration.getInstance().getFieldMapper();
SQLField field = null;
if (fieldMapper != null) {
field = fieldMapper.getSQLFieldForItem(id);
}
// Maybe the id is a field name (deprecated)
if (field == null) {
field = this.getTable().getFieldRaw(id);
 
}
return super.addView(comp, field.getName());
}
 
public JComponent createEditor(final String id) {
if (id.startsWith("(") && id.endsWith(")*")) {
try {
final String table = id.substring(1, id.length() - 2).trim();
final String idEditor = GlobalMapper.getInstance().getIds(table).get(0) + ".editor";
System.out.println("Editor: " + idEditor);
final Class<?> cl = (Class<?>) GlobalMapper.getInstance().get(idEditor);
return (JComponent) cl.newInstance();
} catch (final Exception e) {
189,8 → 265,17
}
}
 
final SQLField field = this.getTable().getFieldRaw(id);
final FieldMapper fieldMapper = PropsConfiguration.getInstance().getFieldMapper();
SQLField field = null;
if (fieldMapper != null) {
field = fieldMapper.getSQLFieldForItem(id);
}
// Maybe the id is a field name (deprecated)
if (field == null) {
field = this.getTable().getFieldRaw(id);
 
}
if (field == null) {
final JLabel jLabel = new JLabelBold("No field " + id);
jLabel.setForeground(Color.RED.darker());
String t = "<html>";
219,7 → 304,16
comp = dobj;
} else if (field.isKey()) {
// foreign
 
final SQLElement foreignElement = getElement().getForeignElement(field.getName());
if (foreignElement == null) {
comp = new JLabelBold("no element for foreignd " + id);
comp.setForeground(Color.RED.darker());
Log.get().severe("no element for foreign " + field.getName());
} else {
comp = new ElementComboBox();
((ElementComboBox) comp).init(foreignElement);
}
comp.setOpaque(false);
} else {
if (Boolean.class.isAssignableFrom(type.getJavaType())) {
288,11 → 382,33
if (label == null) {
label = createLabel(id);
this.labels.put(id, label);
updateUI(id, getRIVDesc(id));
 
final RowItemDesc rivDesc = getRIVDescForId(id);
updateUI(id, rivDesc);
}
return label;
}
 
private RowItemDesc getRIVDescForId(final String id) {
final FieldMapper fieldMapper = PropsConfiguration.getInstance().getFieldMapper();
String t = TranslationManager.getInstance().getTranslationForItem(id);
if (t != null) {
return new RowItemDesc(t, t);
}
String fieldName = null;
if (fieldMapper != null) {
final SQLField sqlFieldForItem = fieldMapper.getSQLFieldForItem(id);
if (sqlFieldForItem != null) {
fieldName = sqlFieldForItem.getName();
}
}
if (fieldName == null) {
fieldName = id;
}
final RowItemDesc rivDesc = getRIVDesc(fieldName);
return rivDesc;
}
 
public JComponent getEditor(final String id) {
JComponent editor = this.editors.get(id);
if (editor == null) {
/trunk/OpenConcerto/src/org/openconcerto/sql/element/TreesOfSQLRows.java
25,6 → 25,7
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.Tuple2;
229,7 → 230,7
res.put(input.getCurrent().getTable(), input.getCurrent());
return null;
}
}, RecursionType.BREADTH_FIRST, false);
}, RecursionType.BREADTH_FIRST, Direction.REFERENT);
}
return res;
}
250,7 → 251,7
res.add(input.getCurrent());
return null;
}
}, RecursionType.DEPTH_FIRST, false);
}, RecursionType.DEPTH_FIRST, Direction.REFERENT);
}
return res;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/DefaultElementSQLObject.java
30,6 → 30,7
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
101,11 → 102,11
}
});
 
this.supprBtn = new JButton(new ImageIcon(this.getClass().getResource("delete.png")));
this.supprBtn = new JButton(new ImageIcon(DefaultElementSQLObject.class.getResource("delete.png")));
this.supprBtn.setToolTipText(getTM().translate("remove"));
this.supprBtn.setOpaque(false);
if (isPlastic)
this.supprBtn.setBorder(null);
this.supprBtn.setBorder(BorderFactory.createEmptyBorder());
this.supprBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElement.java
33,6 → 33,7
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.request.SQLCache;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.sql.sqlobject.SQLTextCombo;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.sql.utils.SQLUtils;
134,8 → 135,10
*/
static final public String DEFERRED_CODE = new String("deferred code");
 
@GuardedBy("this")
private SQLElementDirectory directory;
private String l18nPkgName;
private Class<?> l18nClass;
private Phrase name;
private final SQLTable primaryTable;
// used as a key in SQLElementDirectory so it should be immutable
182,7 → 185,7
throw new DBStructureItemNotFound("table is null for " + this.getClass());
}
this.primaryTable = primaryTable;
this.l18nPkgName = null;
this.setL18nPackageName(null);
this.setDefaultName(name);
this.code = code == null ? createCode() : code;
this.combo = null;
364,7 → 367,7
 
final void setDirectory(final SQLElementDirectory directory) {
// since this method should only be called at the end of SQLElementDirectory.addSQLElement()
assert directory.getElement(this.getTable()) == this;
assert directory == null || directory.getElement(this.getTable()) == this;
synchronized (this) {
if (this.directory != directory) {
if (this.areRelationshipsInited())
374,7 → 377,7
}
}
 
protected final SQLElementDirectory getDirectory() {
public synchronized final SQLElementDirectory getDirectory() {
return this.directory;
}
 
407,12 → 410,29
return this.l18nPkgName;
}
 
public final void setL18nPackageName(Class<?> clazz) {
this.setL18nPackageName(clazz.getPackage().getName());
public final synchronized Class<?> getL18nClass() {
return this.l18nClass;
}
 
public final synchronized void setL18nPackageName(String name) {
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;
}
 
/**
734,6 → 754,16
return this.primaryTable;
}
 
/**
* A code identifying a specific meaning for the table and fields. I.e. it is used by
* {@link #getName() names} and {@link SQLFieldTranslator item metadata}. E.g. if two
* applications use the same table for different purposes (at different times, of course), their
* elements should not share a code. On the contrary, if one application merely adds a field to
* an existing table, the new element should keep the same code so that existing name and
* documentation remain.
*
* @return a code for the table and its meaning.
*/
public synchronized final String getCode() {
if (this.code == DEFERRED_CODE) {
final String createCode = this.createCode();
1579,7 → 1609,7
}
 
@Override
public int hashCode() {
public synchronized int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.primaryTable.hashCode();
1669,6 → 1699,13
this.mdPath = Collections.unmodifiableList(newL);
}
 
/**
* The variants searched to find item metadata by
* {@link SQLFieldTranslator#getDescFor(SQLTable, String, String)}. This allow to configure this
* element to choose between the simultaneously loaded metadata.
*
* @return the variants path.
*/
public synchronized final List<String> getMDPath() {
return this.mdPath;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/FieldExpander.java
72,8 → 72,7
return Collections.emptyList();
 
final List<FieldPath> res = new ArrayList<FieldPath>(e.size());
final Path newPath = new Path(field.getPath());
newPath.add(field.getField());
final Path newPath = field.getPath().add(field.getField());
for (final SQLField f : e)
res.add(new FieldPath(newPath, f.getName()));
return res;
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/SQLBrowserColumn.java
55,6 → 55,7
import java.util.Set;
 
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListSelectionModel;
import javax.swing.Icon;
192,7 → 193,7
this.list.setFont(fontText);
this.list.setSelectionMode(this.getSelectionMode());
JScrollPane scrollPane = new JScrollPane(this.list);
scrollPane.setBorder(null);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
scrollPane.setMinimumSize(new Dimension(60, 100));
this.normalPanel.add(scrollPane, c);
// On ajoute la recherche
305,7 → 306,7
c2.insets = new Insets(0, 2, 1, 4);
c2.weightx = 0;
this.min = new JButton();
this.min.setBorder(null);
this.min.setBorder(BorderFactory.createEmptyBorder());
this.min.setBorderPainted(false);
this.min.setOpaque(false);
this.min.setMargin(new Insets(0, 0, 0, 0));
315,7 → 316,7
getParentBrowser().minimizeUntil(SQLBrowserColumn.this);
}
});
this.min.setIcon(new ImageIcon(this.getClass().getResource("minimize.png")));
this.min.setIcon(new ImageIcon(SQLBrowserColumn.class.getResource("minimize.png")));
headerPanel.add(this.min, c2);
 
c2.gridx++;
392,7 → 393,7
 
c2.gridx++;
final JButton del = new JButton(new ImageIcon(BaseSQLComponent.class.getResource("delete.png")));
del.setBorder(null);
del.setBorder(BorderFactory.createEmptyBorder());
del.setOpaque(false);
del.setContentAreaFilled(false);
del.addActionListener(new ActionListener() {
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/SQLElementNames_fr.xml
1,11 → 1,11
<translations>
<element refid="ilm.sql.users.UserCommonSQLElement-USER_COMMON">
<element refid="org.openconcerto.sql.users.UserCommonSQLElement-USER_COMMON">
<name base="utilisateur" nounClass="masculine" />
</element>
<element refid="ilm.sql.users.rights.RightSQLElement-RIGHT">
<element refid="org.openconcerto.sql.users.rights.RightSQLElement-RIGHT">
<name base="droit" nounClass="masculine" />
</element>
<element refid="ilm.sql.users.rights.UserRightSQLElement-USER_RIGHT">
<element refid="org.openconcerto.sql.users.rights.UserRightSQLElement-USER_RIGHT">
<name base="droit utilisateur" nounClass="masculine">
<variant refids="plural" value="droits utilisateurs" />
</name>
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_en.properties
50,6 → 50,7
noRightToReorder=You''re not allowed to change order
 
editPanel.keepOpen=don''t close the window
editPanel.readOnlySelection=This row is read only
editPanel.inexistentElement=this item doesn't exist
editPanel.cancelError=Error while canceling
editPanel.modifyError=Error while modifying
64,7 → 65,7
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 {0,choice,1#this row'{1,choice,0#|1# and its content}'|1<these {0,number,integer} rows'{1,choice,0#|1# and their contents}'} ?
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.noSelectionOrSort=No selection or list sorted
listPanel.export=List export
listPanel.save=Save the list
76,6 → 77,8
element.list=List of {element__plural}
 
ilist.setColumnsWidth=Adjust columns widths
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} character{0,choice,1#|1<s} too long
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_fr.properties
50,6 → 50,7
noRightToReorder=Vous n''avez pas le droit de changer l'ordre
 
editPanel.keepOpen=ne pas fermer la fenêtre
editPanel.readOnlySelection=Cette ligne est verrouillée
editPanel.inexistentElement=cet élément n'existe pas
editPanel.cancelError=Erreur pendant l''annulation
editPanel.modifyError=Erreur pendant la modification
64,7 → 65,8
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 {0,choice,1#cette ligne'{1,choice,0#|1# et son contenu}'|1<ces {0,number,integer} lignes'{1,choice,0#|1# et leurs contenus}'} ?
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.noSelectionOrSort=Pas de sélection ou liste triée
listPanel.export=Export de la liste
listPanel.save=Sauver la liste
76,6 → 78,8
element.list=Liste {element__de__pluralDefiniteArticle}
 
ilist.setColumnsWidth=Ajuster la largeur des colonnes
ilist.lockRows=Verrouiller les lignes
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
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/SQLElementNames_en.xml
1,11 → 1,11
<translations>
<element refid="ilm.sql.users.UserCommonSQLElement-USER_COMMON">
<element refid="org.openconcerto.sql.users.UserCommonSQLElement-USER_COMMON">
<name base="user" />
</element>
<element refid="ilm.sql.users.rights.RightSQLElement-RIGHT">
<element refid="org.openconcerto.sql.users.rights.RightSQLElement-RIGHT">
<name base="right" />
</element>
<element refid="ilm.sql.users.rights.UserRightSQLElement-USER_RIGHT">
<element refid="org.openconcerto.sql.users.rights.UserRightSQLElement-USER_RIGHT">
<name base="user right">
<variant refids="plural" value="users rights" />
</name>
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/ChangeTable.java
21,6 → 21,7
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTable.Index;
import org.openconcerto.sql.model.SQLType;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Rule;
import org.openconcerto.sql.model.graph.SQLKey;
29,6 → 30,7
import org.openconcerto.utils.ReflectUtils;
import org.openconcerto.utils.cc.ITransformer;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
212,6 → 214,78
return cat(cts, new ChangeRootNameTransformer(r), true).get(0);
}
 
// allow to factor column name from table and FCSpec
public static final class ForeignColSpec {
 
static public ForeignColSpec fromCreateTable(SQLCreateTableBase<?> createTable) {
final List<String> primaryKey = createTable.getPrimaryKey();
if (primaryKey.size() != 1)
throw new IllegalArgumentException("Not exactly one field in the foreign primary key : " + primaryKey);
return new ForeignColSpec(null, new SQLName(createTable.getName()), primaryKey.get(0), null);
}
 
static public ForeignColSpec fromTable(SQLTable foreignTable) {
return fromTable(foreignTable, true);
}
 
static public ForeignColSpec fromTable(SQLTable foreignTable, final boolean absolute) {
if (foreignTable == null)
throw new NullPointerException("null table");
final String defaultVal = foreignTable.getKey().getType().toString(foreignTable.getUndefinedIDNumber());
final SQLName n = absolute ? foreignTable.getSQLName() : new SQLName(foreignTable.getName());
return new ForeignColSpec(null, n, foreignTable.getKey().getName(), defaultVal);
}
 
private String fk;
private final SQLName table;
private final String pk;
private final String defaultVal;
 
public ForeignColSpec(String fk, SQLName table, String pk, String defaultVal) {
super();
this.table = table;
this.setColumnName(fk);
this.pk = pk;
this.defaultVal = defaultVal;
}
 
public final ForeignColSpec setColumnNameFromTable() {
return this.setColumnNameWithSuffix("");
}
 
public final ForeignColSpec setColumnNameWithSuffix(final String suffix) {
return this.setColumnName(SQLKey.PREFIX + getTable().getName() + (suffix.length() == 0 ? "" : "_" + suffix));
}
 
public final ForeignColSpec setColumnName(final String fk) {
if (fk == null)
this.setColumnNameFromTable();
else
this.fk = fk;
return this;
}
 
public final String getColumnName() {
return this.fk;
}
 
public final SQLName getTable() {
return this.table;
}
 
public final String getPrimaryKeyName() {
return this.pk;
}
 
public final String getDefaultVal() {
return this.defaultVal;
}
 
public final FCSpec createFCSpec(final Rule updateRule, final Rule deleteRule) {
return new FCSpec(Collections.singletonList(this.getColumnName()), this.getTable(), Collections.singletonList(this.getPrimaryKeyName()), updateRule, deleteRule);
}
}
 
public static final class FCSpec {
 
static public FCSpec createFromLink(final Link l) {
409,10 → 483,34
final Set<String> typeNames = getSyntax().getTypeNames(javaType);
if (typeNames.size() == 0)
throw new IllegalArgumentException(javaType + " isn't supported by " + getSyntax());
return this.addColumn(name, typeNames.iterator().next(), defaultVal == null ? null : defaultVal.toString(), nullable);
return this.addColumn(name, typeNames.iterator().next(), getNumberDefault(defaultVal), nullable);
}
 
final String getNumberDefault(final Number defaultVal) {
return defaultVal == null ? null : defaultVal.toString();
}
 
/**
* Adds a decimal column.
*
* @param name the name of the column.
* @param precision the total number of digits.
* @param scale the number of digits after the decimal point.
* @param defaultVal the default value of the column, can be <code>null</code>, e.g. 3.14.
* @param nullable whether the column accepts NULL.
* @return this.
* @see SQLSyntax#getDecimal(int, int)
* @see SQLSyntax#getDecimalIntPart(int, int)
*/
public final T addDecimalColumn(String name, int precision, int scale, BigDecimal defaultVal, boolean nullable) {
return this.addColumn(name, getSyntax().getDecimal(precision, scale), getNumberDefault(defaultVal), nullable);
}
 
public final T addBooleanColumn(String name, Boolean defaultVal, boolean nullable) {
return this.addColumn(name, getSyntax().getBooleanType(), SQLType.getBoolean(getSyntax()).toString(defaultVal), nullable);
}
 
/**
* Adds a column.
*
* @param name the name of the column.
491,7 → 589,7
// * addForeignColumn = addColumn + addForeignConstraint
 
public T addForeignColumn(SQLCreateTableBase<?> createTable) {
return this.addForeignColumnWithSuffix("", createTable);
return this.addForeignColumn(ForeignColSpec.fromCreateTable(createTable));
}
 
/**
504,8 → 602,7
* @see #addForeignColumn(String, SQLCreateTableBase)
*/
public T addForeignColumnWithSuffix(String suffix, SQLCreateTableBase<?> createTable) {
final String fk = SQLKey.PREFIX + createTable.getName() + (suffix.length() == 0 ? "" : "_" + suffix);
return this.addForeignColumn(fk, createTable);
return this.addForeignColumn(ForeignColSpec.fromCreateTable(createTable).setColumnNameWithSuffix(suffix));
}
 
/**
519,10 → 616,7
* @see #addForeignColumn(String, SQLName, String, String)
*/
public T addForeignColumn(String fk, SQLCreateTableBase<?> createTable) {
final List<String> primaryKey = createTable.getPrimaryKey();
if (primaryKey.size() != 1)
throw new IllegalArgumentException("Not exactly one field in the foreign primary key : " + primaryKey);
return this.addForeignColumn(fk, new SQLName(createTable.getName()), primaryKey.get(0), null);
return this.addForeignColumn(ForeignColSpec.fromCreateTable(createTable).setColumnName(fk));
}
 
/**
536,10 → 630,18
* @return this.
*/
public T addForeignColumn(String fk, SQLName table, String pk, String defaultVal) {
this.addColumn(fk, this.getSyntax().getIDType() + " DEFAULT " + defaultVal);
return this.addForeignConstraint(fk, table, pk);
return this.addForeignColumn(new ForeignColSpec(fk, table, pk, defaultVal));
}
 
public T addForeignColumn(final ForeignColSpec spec) {
return this.addForeignColumn(spec, null, null);
}
 
public T addForeignColumn(final ForeignColSpec spec, final Rule updateRule, final Rule deleteRule) {
this.addColumn(spec.getColumnName(), this.getSyntax().getIDType() + " DEFAULT " + spec.getDefaultVal());
return this.addForeignConstraint(spec.createFCSpec(updateRule, deleteRule), true);
}
 
public T addForeignColumn(String fk, SQLTable foreignTable) {
return this.addForeignColumn(fk, foreignTable, true);
}
555,11 → 657,7
* @see #addForeignColumn(String, SQLName, String, String)
*/
public T addForeignColumn(String fk, SQLTable foreignTable, final boolean absolute) {
if (foreignTable == null)
throw new NullPointerException("null table for " + fk);
final String defaultVal = foreignTable.getKey().getType().toString(foreignTable.getUndefinedIDNumber());
final SQLName n = absolute ? foreignTable.getSQLName() : new SQLName(foreignTable.getName());
return this.addForeignColumn(fk, n, foreignTable.getKey().getName(), defaultVal);
return this.addForeignColumn(ForeignColSpec.fromTable(foreignTable, absolute).setColumnName(fk));
}
 
public T addUniqueConstraint(final String name, final List<String> cols) {
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/ReOrder.java
123,7 → 123,7
 
// MAYBE return affected IDs
public final boolean exec() throws SQLException {
final UpdateBuilder updateUndef = new UpdateBuilder(this.t).set(this.t.getOrderField().getName(), MIN_ORDER.toPlainString());
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>() {
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/CreateUser.java
New file
0,0 → 1,56
/*
* 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.utils;
 
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLSystem;
 
import java.util.ArrayList;
import java.util.List;
 
public class CreateUser {
 
private final String name, password;
private final ChangePrivileges grant;
 
public CreateUser(String name, String pass) {
super();
this.name = name;
this.password = pass;
this.grant = new ChangePrivileges(name);
}
 
public final ChangePrivileges getGrant() {
return this.grant;
}
 
public final List<String> getStatements(final DBSystemRoot sysRoot) {
final List<String> res = new ArrayList<String>();
final String qName = SQLBase.quoteIdentifier(this.name);
// TODO move SQLBase.quoteString() to DBSystemRoot
final String passKeyWord = sysRoot.getServer().getSQLSystem() == SQLSystem.MYSQL ? " IDENTIFIED BY " : " PASSWORD ";
res.add("CREATE USER " + qName + passKeyWord + SQLBase.quoteStringStd(this.password));
res.addAll(this.grant.getStatements(sysRoot));
return res;
}
 
public final void execute(final DBSystemRoot sysRoot) {
final SQLDataSource ds = sysRoot.getDataSource();
for (final String s : this.getStatements(sysRoot)) {
ds.execute(s);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/ChangePrivileges.java
New file
0,0 → 1,176
/*
* 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.utils;
 
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.CollectionMap2.Mode;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.SetMap;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
 
/**
* Allow to grant or revoke privileges of users/roles.
*
* @author Sylvain
*/
public class ChangePrivileges {
 
static public enum PrivelegeAction {
GRANT, REVOKE
}
 
static public enum TablePrivilege {
SELECT, INSERT, UPDATE, DELETE
}
 
private final String name;
private final SetMap<SQLName, TablePrivilege> privilegesToGrant;
private final SetMap<SQLName, TablePrivilege> privilegesToRevoke;
 
public ChangePrivileges(String userName) {
super();
this.name = userName;
this.privilegesToGrant = new SetMap<SQLName, TablePrivilege>(Mode.NULL_MEANS_ALL);
this.privilegesToRevoke = new SetMap<SQLName, TablePrivilege>(Mode.NULL_MEANS_ALL);
}
 
public final ChangePrivileges grantTablePrivilege(final SQLName t, final TablePrivilege priv) {
return this.changeTablePrivilege(PrivelegeAction.GRANT, t, priv);
}
 
public final ChangePrivileges revokeTablePrivilege(final SQLName t, final TablePrivilege priv) {
return this.changeTablePrivilege(PrivelegeAction.REVOKE, t, priv);
}
 
public final ChangePrivileges changeTablePrivilege(final PrivelegeAction grant, final DBRoot r, final TablePrivilege priv) {
this.changeTablePrivilege(grant, new SQLName(r.getName()), priv);
return this;
}
 
public final ChangePrivileges changeTablePrivilege(final PrivelegeAction grant, final SQLTable t, final TablePrivilege priv) {
return this.changeTablePrivilege(grant, t.getSQLNameUntilDBRoot(true), priv);
}
 
/**
* Grant or revoke a table privilege for the passed object.
*
* @param grant whether to grant or revoke.
* @param t size of 1 refer to root, size of 2 to a table.
* @param priv the privilege to change.
* @return this.
* @throws IllegalArgumentException if size of t is invalid.
*/
public final ChangePrivileges changeTablePrivilege(final PrivelegeAction grant, final SQLName t, final TablePrivilege priv) throws IllegalArgumentException {
return this.addTablePrivilege(grant == PrivelegeAction.GRANT ? this.privilegesToGrant : this.privilegesToRevoke, t, priv);
}
 
private final ChangePrivileges addTablePrivilege(final SetMap<SQLName, TablePrivilege> m, final SQLName t, final TablePrivilege priv) throws IllegalArgumentException {
if (t.getItemCount() == 0 || t.getItemCount() > 2)
throw new IllegalArgumentException("not root.table :" + t);
if (priv == null)
m.put(t, null);
else
m.add(t, priv);
return this;
}
 
/**
* Make sure this instance won't grant nor revoke any privilege from the passed object. I.e.
* only useful if <code>t</code> was passed to a method the change table privileges.
*
* @param t a root or table name.
* @return this.
*/
public final ChangePrivileges removeTablePrivilege(final SQLName t) {
if (t.getItemCount() != 2)
throw new IllegalArgumentException("not root.table :" + t);
this.privilegesToGrant.remove(t);
this.privilegesToRevoke.remove(t);
return this;
}
 
private final Set<String> getTableNames(final DBSystemRoot sysRoot, final SQLName name) {
final String rootName = name.getFirst();
 
final Set<String> tables;
if (name.getItemCount() == 2) {
tables = Collections.singleton(name.getName());
} else {
assert name.getItemCount() == 1;
tables = sysRoot.getRoot(rootName).getChildrenNames();
}
 
return tables;
}
 
public final List<String> getStatements(final DBSystemRoot sysRoot) {
final List<String> res = new ArrayList<String>();
final String qName = SQLBase.quoteIdentifier(this.name);
final Set<String> roots = new HashSet<String>();
for (final Entry<SQLName, Set<TablePrivilege>> e : this.privilegesToGrant.entrySet()) {
final String privs = e.getValue() == null ? "ALL" : CollectionUtils.join(e.getValue(), ", ");
final SQLName name = e.getKey();
final String rootName = name.getFirst();
for (final String tableName : getTableNames(sysRoot, name)) {
res.add("GRANT " + privs + " ON " + new SQLName(rootName, tableName).quote() + " TO " + qName);
}
roots.add(rootName);
}
if (sysRoot.getServer().getSQLSystem() == SQLSystem.POSTGRESQL) {
for (final String schema : roots) {
res.add(0, "GRANT USAGE ON SCHEMA " + SQLBase.quoteIdentifier(schema) + " TO " + qName);
}
}
roots.clear();
// if privilege specified twice, revoke
for (final Entry<SQLName, Set<TablePrivilege>> e : this.privilegesToRevoke.entrySet()) {
final boolean allPriv = e.getValue() == null;
final String privs = allPriv ? "ALL" : CollectionUtils.join(e.getValue(), ", ");
 
final SQLName name = e.getKey();
final String rootName = name.getFirst();
for (final String tableName : getTableNames(sysRoot, name)) {
res.add("REVOKE " + privs + " ON " + new SQLName(rootName, tableName).quote() + " FROM " + qName);
}
 
if (allPriv && name.getItemCount() == 1)
roots.add(rootName);
}
if (sysRoot.getServer().getSQLSystem() == SQLSystem.POSTGRESQL) {
for (final String schema : roots) {
res.add(0, "REVOKE USAGE ON SCHEMA " + SQLBase.quoteIdentifier(schema) + " FROM " + qName);
}
}
return res;
}
 
public final void execute(final DBSystemRoot sysRoot) {
final SQLDataSource ds = sysRoot.getDataSource();
for (final String s : this.getStatements(sysRoot)) {
ds.execute(s);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBItemFileCache.java
19,6 → 19,8
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
 
67,10 → 69,15
if (root == null)
throw new NullPointerException("null parent");
this.root = root;
this.f = f;
// TODO limit to read permission
final List<File> ancs = AccessController.doPrivileged(new PrivilegedAction<List<File>>() {
@Override
public List<File> run() {
if (f.exists() && !f.isDirectory())
throw new IllegalArgumentException("f is not a directory: " + f);
this.f = f;
// pgsql_127.0.0.1/n_"Controle"/ is Base, mysql_127.0.0.1/n_"Ideation_2007"/n_ is Schema
// pgsql_127.0.0.1/n_"Controle"/ is Base, mysql_127.0.0.1/n_"Ideation_2007"/n_ is
// Schema
final File rel;
try {
rel = new File(FileUtils.relative(root.getSystemDir(), f));
77,7 → 84,9
} catch (IOException e) {
throw new IllegalStateException("could not rel " + f + " to " + root, e);
}
final List<File> ancs = FileUtils.getAncestors(rel);
return FileUtils.getAncestors(rel);
}
});
if (ancs.get(0).getName().equals(".."))
throw new IllegalArgumentException(f + " is not beneath " + root);
if (ancs.get(0).getName().equals("."))
210,8 → 219,14
}
};
}
return FileUtils.list(this.getValidDir(), diff, fileFilter);
// TODO limit to read permission
return AccessController.doPrivileged(new PrivilegedAction<List<File>>() {
@Override
public List<File> run() {
return FileUtils.list(getValidDir(), diff, fileFilter);
}
});
}
 
private int getDiff(HierarchyLevel l) {
final EnumOrderedSet<HierarchyLevel> all = HierarchyLevel.getAll();
/trunk/OpenConcerto/src/org/openconcerto/sql/model/FieldMapper.java
18,6 → 18,7
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
59,9 → 60,14
if (fieldName == null) {
return null;
}
try {
final SQLField field = this.root.getField(fieldName);
return field;
} catch (Exception e) {
Log.get().warning("No field found " + fieldName + " (" + e.getMessage() + ")");
}
return null;
}
 
public SQLTable getSQLTableForItem(String id) {
final String tableName = this.tableMapping.get(id);
151,4 → 157,17
}
}
 
public String getItemMapping() {
List<String> l = new ArrayList<String>();
l.addAll(this.itemMapping.keySet());
Collections.sort(l);
StringBuilder b = new StringBuilder(l.size() * 50);
for (String string : l) {
b.append(string);
b.append(" : ");
b.append(this.itemMapping.get(string));
b.append('\n');
}
return b.toString();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/UndefinedRowValuesCache.java
14,6 → 14,7
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.Log;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.request.MultipleSQLSelectExecutor;
import org.openconcerto.utils.ExceptionHandler;
46,9 → 47,11
if (rv == null) {
rv = new SQLRowValues(t);
final SQLRow undefRow = t.getRow(t.getUndefinedID());
if (undefRow == null)
throw new IllegalStateException(t.getSQLName() + " doesn't contain undef ID " + t.getUndefinedID());
if (undefRow == null) {
Log.get().warning(t.getSQLName() + " doesn't contain undef ID " + t.getUndefinedID());
} else {
getDirectory().getElement(t).loadAllSafe(rv, undefRow);
}
this.map.put(t, rv);
}
return rv;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLField.java
33,6 → 33,8
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import net.jcip.annotations.ThreadSafe;
 
48,6 → 50,9
@ThreadSafe
public class SQLField extends SQLIdentifier implements FieldRef, IFieldPath {
 
// nextVal('"SCHEMA"."seqName"'::regclass);
static private final Pattern SEQ_PATTERN = Pattern.compile("nextval\\('(.+)'.*\\)");
 
static final SQLField create(SQLTable t, ResultSet rs) throws SQLException {
final String fieldName = rs.getString("COLUMN_NAME");
 
232,6 → 237,42
return Collections.unmodifiableMap(this.infoSchemaCols);
}
 
/**
* The sequence linked to this field. I.e. that sequence will be dropped if this field is.
*
* @return the quoted name of the sequence, <code>null</code> if none.
*/
public final SQLName getOwnedSequence() {
return this.getOwnedSequence(false);
}
 
public final SQLName getOwnedSequence(final boolean allowRequest) {
final SQLSystem sys = getServer().getSQLSystem();
if (sys == SQLSystem.H2) {
final String name = (String) this.infoSchemaCols.get("SEQUENCE_NAME");
if (name != null)
return new SQLName(name);
} else if (sys == SQLSystem.POSTGRESQL) {
if (allowRequest) {
final String req = "SELECT pg_get_serial_sequence(" + getTable().getBase().quoteString(getTable().getSQLName().quote()) + ", " + getTable().getBase().quoteString(this.getName()) + ")";
final String name = (String) getDBSystemRoot().getDataSource().executeScalar(req);
if (name != null)
return SQLName.parse(name);
} else {
final String def = ((String) this.getDefaultValue()).trim();
if (def.startsWith("nextval")) {
final Matcher matcher = SEQ_PATTERN.matcher(def);
if (matcher.matches()) {
return SQLName.parse(matcher.group(1));
} else {
throw new IllegalStateException("could not parse: " + def + " with " + SEQ_PATTERN.pattern());
}
}
}
}
return null;
}
 
public synchronized Object getDefaultValue() {
return this.defaultValue;
}
250,8 → 291,14
return this.getTable().getKeys().contains(this);
}
 
/**
* Is this the one and only field in the primary key of its table.
*
* @return <code>true</code> if this is part of the primary key, and the primary key has no
* other fields.
*/
public boolean isPrimaryKey() {
return this.getTable().getKey() == this;
return this.getTable().getPrimaryKeys().equals(Collections.singleton(this));
}
 
public final SQLTable getForeignTable() {
372,7 → 419,7
 
@Override
public Path getPath() {
return new Path(getTable());
return Path.get(getTable());
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLFilter.java
164,6 → 164,12
}
}
 
public final List<Set<SQLRow>> getRows() {
synchronized (this.filteredIDs) {
return new ArrayList<Set<SQLRow>>(this.filteredIDs);
}
}
 
public final Set<SQLRow> getLeaf() {
synchronized (this.filteredIDs) {
return CollectionUtils.getFirst(this.filteredIDs);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLServer.java
25,6 → 25,8
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.change.CollectionChangeEventCreator;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
255,10 → 257,16
for (final DBItemFileCache savedBase : cache.getServerCache().getSavedDesc(SQLBase.class)) {
final String savedBaseName = savedBase.getName();
if (!cats.contains(savedBaseName) && (namesToRefresh == null || namesToRefresh.contains(savedBaseName)) && this.getDBSystemRoot().createNode(this, savedBaseName)) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
savedBase.delete();
return null;
}
});
}
}
}
 
// fire once all the bases are loaded so that the graph is coherent
return this.getDBSystemRoot().getGraph().atomicRefresh(new Callable<Map<String, TablesMap>>() {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRow.java
414,6 → 414,12
}
 
@Override
public int getForeignID(String fieldName) {
final SQLRow foreignRow = this.getForeignRow(fieldName, SQLRowMode.NO_CHECK);
return foreignRow == null ? SQLRow.NONEXISTANT_ID : foreignRow.getID();
}
 
@Override
public boolean isForeignEmpty(String fieldName) {
final SQLRow foreignRow = this.getForeignRow(fieldName, SQLRowMode.NO_CHECK);
return foreignRow == null || foreignRow.isUndefined();
547,7 → 553,7
* @see #getDistantRows(List)
*/
public SQLRow getDistantRow(List<String> path) {
return this.getDistantRow(new Path(this.getTable()).addTables(path));
return this.getDistantRow(Path.get(this.getTable()).addTables(path));
}
 
public SQLRow getDistantRow(final Path path) {
565,7 → 571,7
* @throws IllegalArgumentException si le path est mauvais.
*/
public Set<SQLRow> getDistantRows(List<String> path) {
return this.getDistantRows(new Path(this.getTable()).addTables(path));
return this.getDistantRows(Path.get(this.getTable()).addTables(path));
}
 
public Set<SQLRow> getDistantRows(final Path path) {
594,7 → 600,7
* @return un ensemble de List de SQLRow.
*/
public Set<List<SQLRow>> getRowsOnPath(final List<String> path, final List<? extends Collection<String>> fields) {
return this.getRowsOnPath(new Path(this.getTable()).addTables(path), fields);
return this.getRowsOnPath(Path.get(this.getTable()).addTables(path), fields);
}
 
public Set<List<SQLRow>> getRowsOnPath(final Path p, final List<? extends Collection<String>> fields) {
612,7 → 618,7
// ne pas oublier de sélectionner notre ligne
where = where.and(this.getWhere());
 
final SQLSelect select = new SQLSelect(this.getTable().getBase());
final SQLSelect select = new SQLSelect();
 
final List<Collection<String>> fieldsCols = new ArrayList<Collection<String>>(pathSize);
for (int i = 0; i < pathSize; i++) {
750,7 → 756,7
}
 
final SQLTable src = refField.getTable();
final SQLSelect sel = new SQLSelect(this.getTable().getBase());
final SQLSelect sel = new SQLSelect();
if (fields == null)
sel.addSelectStar(src);
else {
784,7 → 790,7
public Collection<SQLRow> followLink(Link l, Direction direction) {
// Path checks that one end of l is this table and that direction is valid (e.g. not ANY for
// self-reference links)
final boolean backwards = new Path(getTable()).add(l, direction).isBackwards(0);
final boolean backwards = Path.get(getTable()).add(l, direction).isBackwards(0);
if (backwards)
return getReferentRows(l.getSingleField());
else
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxH2.java
61,12 → 61,14
return false;
 
final String def = ((String) f.getDefaultValue()).toUpperCase();
return f.getType().getJavaType() == Long.class && def.contains("NEXT VALUE") && def.contains("SYSTEM_SEQUENCE");
// we used to use IDENTITY which translate to long
return (f.getType().getJavaType() == Integer.class || f.getType().getJavaType() == Long.class) && def.contains("NEXT VALUE") && def.contains("SYSTEM_SEQUENCE");
}
 
@Override
public String getAuto() {
return " IDENTITY";
// IDENTITY means long
return " SERIAL";
}
 
@Override
102,7 → 104,11
// MAYBE implement AlterTableAlterColumn.CHANGE_ONLY_TYPE
final String newDef = toAlter.contains(Properties.DEFAULT) ? defaultVal : getDefault(f, type);
final boolean newNullable = toAlter.contains(Properties.NULLABLE) ? nullable : getNullable(f);
res.add("ALTER COLUMN " + f.getQuotedName() + " " + getFieldDecl(type, newDef, newNullable));
final SQLName seqName = f.getOwnedSequence();
// sequence is used for the default so if default change, remove it (same behaviour than
// H2)
final String seqSQL = seqName == null || toAlter.contains(Properties.DEFAULT) ? "" : " SEQUENCE " + seqName.quote();
res.add("ALTER COLUMN " + f.getQuotedName() + " " + getFieldDecl(type, newDef, newNullable) + seqSQL);
} else {
if (toAlter.contains(Properties.DEFAULT))
res.add(this.setDefault(f, defaultVal));
233,7 → 239,7
@Override
public String getColumnsQuery(SQLBase b, TablesMap tables) {
return "SELECT \"" + INFO_SCHEMA_NAMES_KEYS.get(0) + "\", \"" + INFO_SCHEMA_NAMES_KEYS.get(1) + "\", \"" + INFO_SCHEMA_NAMES_KEYS.get(2)
+ "\" , \"CHARACTER_SET_NAME\", \"COLLATION_NAME\" from INFORMATION_SCHEMA.\"COLUMNS\" " + getTablesMapJoin(b, tables) + " where " + getInfoSchemaWhere(b);
+ "\" , \"CHARACTER_SET_NAME\", \"COLLATION_NAME\", \"SEQUENCE_NAME\" from INFORMATION_SCHEMA.\"COLUMNS\" " + getTablesMapJoin(b, tables) + " where " + getInfoSchemaWhere(b);
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLType.java
100,6 → 100,14
 
static private final Map<List<String>, SQLType> instances = new HashMap<List<String>, SQLType>();
 
// useful when no SQLBase is known
public static SQLType getBoolean(final SQLSyntax s) {
// TODO use get() once it accepts a SQLSyntax
final SQLType res = new BooleanType(Types.BOOLEAN, 1, null, Boolean.class);
res.setSyntax(s);
return res;
}
 
public static SQLType get(final int type, final int size) {
return get(null, type, size, null, null);
}
214,16 → 222,25
return this.typeName;
}
 
// TODO remove once quoteString() is in SQLSyntax
private final void setBase(SQLBase base) {
// set only once
assert this.base == null;
if (base != null) {
this.base = base;
this.syntax = this.base.getServer().getSQLSystem().getSyntax();
this.setSyntax(this.base.getServer().getSQLSystem().getSyntax());
}
}
 
public final SQLBase getBase() {
private final void setSyntax(SQLSyntax s) {
// set only once
assert this.syntax == null;
if (s != null) {
this.syntax = s;
}
}
 
private final SQLBase getBase() {
return this.base;
}
 
231,6 → 248,10
return this.syntax;
}
 
protected final String quoteString(String s) {
return SQLBase.quoteString(this.getBase(), s);
}
 
@Override
public boolean equals(Object obj) {
return equals(obj, null);
355,9 → 376,11
* @throws IllegalArgumentException if o is not valid.
*/
public final void check(Object o) {
if (!isValid(o))
throw new IllegalArgumentException(o + " is not valid for " + this);
if (!isValid(o)) {
final String className = o == null ? "" : "(" + o.getClass() + ")";
throw new IllegalArgumentException(o + className + " is not valid for " + this);
}
}
 
/**
* Test whether o is valid for this type. Ie does o is an instance of getJavaType().
421,7 → 444,7
protected String toStringRaw(Object o) {
if (this.getSyntax().getSystem() == SQLSystem.MSSQL) {
// 'true'
return this.getBase().quoteString(o.toString());
return this.quoteString(o.toString());
} else
return super.toStringRaw(o);
}
525,7 → 548,7
 
@Override
protected String toStringRaw(Object o) {
return SQLBase.quoteString(this.getBase(), (String) o);
return this.quoteString((String) o);
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesListFetcher.java
13,6 → 13,7
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
19,6 → 20,7
import org.openconcerto.sql.model.graph.Step;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.Tuple2;
31,6 → 33,7
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
42,6 → 45,7
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
67,7 → 71,7
// path -> longest referent only path
// i.e. map each path to the main fetcher or a referent graft
final Map<Path, Path> handledPaths = new HashMap<Path, Path>();
final Path emptyPath = new Path(graph.getTable());
final Path emptyPath = Path.get(graph.getTable());
handledPaths.put(emptyPath, emptyPath);
// find out referent only paths (yellow in the diagram)
graph.getGraph().walk(graph, null, new ITransformer<State<Object>, Object>() {
82,7 → 86,7
}
return null;
}
}, RecursionType.DEPTH_FIRST, false);
}, RecursionType.DEPTH_FIRST, Direction.REFERENT);
 
// find out needed grafts
final CollectionMap<Path, SQLRowValuesListFetcher> grafts = new CollectionMap<Path, SQLRowValuesListFetcher>();
127,7 → 131,7
}
return null;
}
}, RecursionType.BREADTH_FIRST, null, false);
}, RecursionType.BREADTH_FIRST, Direction.ANY, false);
 
final Set<Path> refPaths = new HashSet<Path>(handledPaths.values());
// remove the main fetcher
171,32 → 175,31
private static Path computePath(SQLRowValues graph) {
// check that there's only one referent for each row
// (otherwise huge joins, e.g. LOCAL<-CPI,SOURCE,RECEPTEUR,etc.)
final Path res = new Path(graph.getTable());
graph.getGraph().walk(graph, res, new ITransformer<State<Path>, Path>() {
final AtomicReference<Path> res = new AtomicReference<Path>(null);
graph.getGraph().walk(graph, null, new ITransformer<State<Path>, Path>() {
@Override
public Path transformChecked(State<Path> input) {
final Collection<SQLRowValues> referentRows = input.getCurrent().getReferentRows();
if (referentRows.size() > 1) {
final int size = referentRows.size();
if (size > 1) {
// remove the foreign rows which are all the same (since they point to
// current) so the exn is more legible
final List<SQLRowValues> toPrint = SQLRowValues.trim(referentRows);
throw new IllegalArgumentException(input.getCurrent() + " is referenced by " + toPrint + "\nat " + input.getPath());
} else if (referentRows.size() == 0) {
input.getAcc().append(input.getPath());
} else if (size == 0) {
if (res.get() == null)
res.set(input.getPath());
else
throw new IllegalStateException();
}
return input.getAcc();
}
}, RecursionType.BREADTH_FIRST, false);
return res;
}, RecursionType.BREADTH_FIRST, Direction.REFERENT, true);
// since includeStart=true
assert res.get() != null;
return res.get();
}
 
private static Path createPath(final SQLTable t, SQLField... fields) {
final Path res = new Path(t);
for (final SQLField f : fields)
res.add(f);
return res;
}
 
static private final CollectionMap<Tuple2<Path, Number>, SQLRowValues> createCollectionMap() {
// we need a List in merge()
return new CollectionMap<Tuple2<Path, Number>, SQLRowValues>(new ArrayList<SQLRowValues>(8));
248,10 → 251,6
this(graph, referents ? computePath(graph) : null);
}
 
public SQLRowValuesListFetcher(SQLRowValues graph, final SQLField... fields) {
this(graph, createPath(graph.getTable(), fields));
}
 
/**
* Construct a new instance.
*
277,7 → 276,7
SQLRowValuesListFetcher(final SQLRowValues graph, final Path referentPath, final boolean prune) {
super();
this.graph = graph.deepCopy();
this.descendantPath = referentPath == null ? new Path(graph.getTable()) : referentPath;
this.descendantPath = referentPath == null ? Path.get(graph.getTable()) : referentPath;
if (!this.descendantPath.isDirection(Direction.REFERENT))
throw new IllegalArgumentException("path is not (exclusively) referent : " + this.descendantPath);
final SQLRowValues descRow = this.graph.followPath(this.descendantPath);
297,7 → 296,7
}
return null;
}
}, RecursionType.BREADTH_FIRST, true);
}, RecursionType.BREADTH_FIRST, Direction.FOREIGN);
}
 
// always need IDs
348,6 → 347,10
return this.graph;
}
 
public final Path getReferentPath() {
return this.descendantPath;
}
 
/**
* Whether to include undefined rows (of tables other than the graph's).
*
425,7 → 428,7
* @return this.
*/
public final SQLRowValuesListFetcher setOrdered(final boolean b) {
this.setOrder(b ? Collections.singleton(new Path(getGraph().getTable())) : Collections.<Path> emptySet(), true);
this.setOrder(b ? Collections.singleton(Path.get(getGraph().getTable())) : Collections.<Path> emptySet(), true);
this.setReferentsOrdered(b, false);
return this;
}
477,7 → 480,7
}
 
public final SQLRowValuesListFetcher graft(final SQLRowValuesListFetcher other) {
return this.graft(other, new Path(getGraph().getTable()));
return this.graft(other, Path.get(getGraph().getTable()));
}
 
public final SQLRowValuesListFetcher graft(final SQLRowValues other, Path graftPath) {
517,7 → 520,8
// same capacity (or any other predicate)
 
if (!this.grafts.containsKey(graftPath)) {
this.grafts.put(graftPath, new HashMap<Path, SQLRowValuesListFetcher>(4));
// allow getFetchers() to use a list, easing tests and avoiding using equals()
this.grafts.put(graftPath, new LinkedHashMap<Path, SQLRowValuesListFetcher>(4));
} else {
final Map<Path, SQLRowValuesListFetcher> map = this.grafts.get(graftPath);
// e.g. fetching *BATIMENT* <- LOCAL and *BATIMENT* <- LOCAL <- CPI (with different
541,7 → 545,7
}
 
public final Collection<SQLRowValuesListFetcher> ungraft() {
return this.ungraft(new Path(getGraph().getTable()));
return this.ungraft(Path.get(getGraph().getTable()));
}
 
public final Collection<SQLRowValuesListFetcher> ungraft(final Path graftPath) {
560,6 → 564,44
return Collections.unmodifiableMap(this.grafts.get(graftPath));
}
 
/**
* Get instances which fetch the {@link Path#getLast() last table} of the passed path. E.g.
* useful if you want to add a where to a join. This method is recursively called on
* {@link #getGrafts(Path) grafts} thus the returned paths may be fetched by grafts.
*
* @param fetchedPath a path starting by this table.
* @return all instances indexed by the graft path, i.e. <code>fetchedPath</code> is between
* with it and (it+fetchers.{@link #getReferentPath()}).
*/
public final ListMap<Path, SQLRowValuesListFetcher> getFetchers(final Path fetchedPath) {
final ListMap<Path, SQLRowValuesListFetcher> res = new ListMap<Path, SQLRowValuesListFetcher>();
if (this.getGraph().followPath(fetchedPath) != null)
res.add(Path.get(getGraph().getTable()), this);
// search grafts
for (final Entry<Path, Map<Path, SQLRowValuesListFetcher>> e : this.grafts.entrySet()) {
final Path graftPlace = e.getKey();
if (fetchedPath.startsWith(graftPlace) && fetchedPath.length() > graftPlace.length()) {
final Path rest = fetchedPath.subPath(graftPlace.length());
// we want requests that use the last step of fetchedPath
assert rest.length() > 0;
for (final Entry<Path, SQLRowValuesListFetcher> e2 : e.getValue().entrySet()) {
final Path refPath = e2.getKey();
final SQLRowValuesListFetcher graft = e2.getValue();
if (refPath.startsWith(rest)) {
res.add(graftPlace, graft);
} else if (rest.startsWith(refPath)) {
// otherwise rest == refPath and the above if would have been executed
assert rest.length() > refPath.length();
for (final Entry<Path, List<SQLRowValuesListFetcher>> e3 : graft.getFetchers(rest).entrySet()) {
res.addAll(graftPlace.append(e3.getKey()), e3.getValue());
}
}
}
}
}
return res;
}
 
private final void addFields(final SQLSelect sel, final SQLRowValues vals, final String alias) {
for (final String fieldName : vals.getFields())
sel.addSelect(new AliasedField(vals.getTable().getField(fieldName), alias));
630,7 → 672,7
// and the reading of the resultSet
private <S> void walk(final S sel, final ITransformer<State<S>, S> transf) {
// walk through foreign keys and never walk back (use graft())
this.getGraph().getGraph().walk(this.getGraph(), sel, transf, RecursionType.BREADTH_FIRST, true);
this.getGraph().getGraph().walk(this.getGraph(), sel, transf, RecursionType.BREADTH_FIRST, Direction.FOREIGN);
// walk starting backwards but allowing forwards
this.getGraph().getGraph().walk(this.getGraph(), sel, new ITransformer<State<S>, S>() {
@Override
645,7 → 687,7
throw new SQLRowValuesCluster.StopRecurseException().setCompletely(false);
return transf.transformChecked(input);
}
}, RecursionType.BREADTH_FIRST, null, false);
}, RecursionType.BREADTH_FIRST, Direction.ANY, false);
}
 
// models the graph, so that we don't have to walk it for each row
867,7 → 909,7
for (final Entry<Path, Map<Path, SQLRowValuesListFetcher>> graftPlaceEntry : this.grafts.entrySet()) {
// e.g. BATIMENT
final Path graftPlace = graftPlaceEntry.getKey();
final Path mapPath = new Path(graftPlace.getLast());
final Path mapPath = Path.get(graftPlace.getLast());
// list of BATIMENT to only fetch what's necessary
final Set<Number> ids = new HashSet<Number>();
// byRows is common to all grafts to support CPI -> LOCAL -> BATIMENT and RECEPTEUR
877,9 → 919,8
// children and if we want their DOSSIER they must be grafted on each line.
final CollectionMap<Tuple2<Path, Number>, SQLRowValues> byRows = createCollectionMap();
for (final SQLRowValues vals : merged) {
final SQLRowValues graftPlaceVals = vals.followPath(graftPlace);
// happens when grafting on optional row
if (graftPlaceVals != null) {
// can be empty when grafting on optional row
for (final SQLRowValues graftPlaceVals : vals.followPath(graftPlace, CreateMode.CREATE_NONE, false)) {
ids.add(graftPlaceVals.getIDNumber());
byRows.put(Tuple2.create(mapPath, graftPlaceVals.getIDNumber()), graftPlaceVals);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntax.java
148,6 → 148,7
 
public abstract boolean isAuto(SQLField f);
 
// should return an int4 not null with automatic values
public abstract String getAuto();
 
public String getIDType() {
327,7 → 328,7
* @return the part after "CREATE UNIQUE INDEX foo ".
*/
protected String getCreateIndex(final String cols, final SQLName tableName, Index i) {
return i.getTable().getBase().quote("ON %i" + cols, tableName);
return "ON " + tableName.quote() + cols;
}
 
/**
659,10 → 660,27
// cannot rename since some systems won't allow it in the same ALTER TABLE
public abstract List<String> getAlterField(SQLField f, Set<Properties> toAlter, String type, String defaultVal, Boolean nullable);
 
/**
* The decimal, arbitrary precision, SQL type.
*
* @param precision the total number of digits.
* @param scale the number of digits after the decimal point.
* @return the SQL type.
* @see #getDecimalIntPart(int, int)
*/
public String getDecimal(int precision, int scale) {
return " DECIMAL(" + precision + "," + scale + ")";
}
 
/**
* The decimal, arbitrary precision, SQL type.
*
* @param intPart the number of digits before the decimal point; NOTE this is not the precision
* as in SQL.
* @param fractionalPart the number of digits after the decimal point.
* @return the SQL type.
* @see #getDecimal(int, int)
*/
public final String getDecimalIntPart(int intPart, int fractionalPart) {
return getDecimal(intPart + fractionalPart, fractionalPart);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValues.java
21,9 → 21,11
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.model.graph.Step;
import org.openconcerto.sql.request.Inserter;
import org.openconcerto.sql.request.Inserter.Insertion;
import org.openconcerto.sql.request.Inserter.ReturnMode;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.utils.ReOrder;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CopyUtils;
537,6 → 539,12
return NumberUtils.areNumericallyEqual(id, undefID);
}
 
@Override
public int getForeignID(String fieldName) {
final SQLRowAccessor foreign = getForeign(fieldName);
return foreign == null ? SQLRow.NONEXISTANT_ID : foreign.getID();
}
 
public boolean isDefault(String fieldName) {
return SQL_DEFAULT.equals(this.getObject(fieldName));
}
955,7 → 963,7
 
@Override
public final Collection<SQLRowValues> followLink(final Link l, final Direction direction) {
return this.followPath(new Path(getTable()).add(l, direction), CreateMode.CREATE_NONE, false);
return this.followPath(Path.get(getTable()).add(l, direction), CreateMode.CREATE_NONE, false);
}
 
/**
1411,11 → 1419,22
*
* @param other another rowValues.
* @return <code>true</code> if both graph are equals.
* @see #getGraphFirstDifference(SQLRowValues)
*/
public final boolean equalsGraph(final SQLRowValues other) {
return this.getGraph().equals(this, other);
return this.getGraphFirstDifference(other) == null;
}
 
/**
* Return the first difference between this graph and another.
*
* @param other another instance.
* @return the first difference, <code>null</code> if equals.
*/
public final String getGraphFirstDifference(final SQLRowValues other) {
return this.getGraph().getFirstDifference(this, other);
}
 
final boolean equalsJustThis(final SQLRowValues o) {
// NO_COPY since foreign rows are handled by SQLRowValuesCluster.equals()
// LinkedHashMap.equals() does not compare the order of entries, which is fine since
1658,38 → 1677,7
return newID;
}
 
public static final class Insertion<T> {
private final List<T> list;
private final int count;
 
private Insertion(List<T> res, int count) {
super();
this.list = res;
this.count = count;
assert res == null || res.size() == count;
}
 
/**
* The number of rows inserted.
*
* @return number of rows inserted.
*/
public final int getCount() {
return this.count;
}
 
/**
* The list of inserted rows. Can be <code>null</code> if it couldn't be retrieved, e.g.
* MySQL only supports single primary key.
*
* @return the list of inserted rows, or <code>null</code>.
*/
public final List<T> getRows() {
return this.list;
}
}
 
/**
* Insert rows in the passed table.
*
* @param t a table, eg /LOCAL/.
1700,8 → 1688,9
*/
@SuppressWarnings("unchecked")
public static final List<Number> insertIDs(final SQLTable t, final String sql) throws SQLException {
final Insertion<?> res = insert(t, sql, true);
if (t.isRowable())
final boolean rowable = t.isRowable();
final Insertion<?> res = insert(t, sql, rowable ? ReturnMode.FIRST_FIELD : ReturnMode.NO_FIELDS);
if (rowable)
return ((Insertion<Number>) res).getRows();
else
return null;
1717,7 → 1706,7
*/
@SuppressWarnings("unchecked")
public static final Insertion<Object[]> insert(final SQLTable t, final String sql) throws SQLException {
return (Insertion<Object[]>) insert(t, sql, false);
return (Insertion<Object[]>) insert(t, sql, ReturnMode.ALL_FIELDS);
}
 
/**
1730,65 → 1719,13
* @throws SQLException if an error occurs while inserting.
*/
public static final int insertCount(final SQLTable t, final String sql) throws SQLException {
return insert(t, sql, null).getCount();
return insert(t, sql, ReturnMode.NO_FIELDS).getCount();
}
 
// if scalar is null primary keys aren't fetched
private static final Insertion<?> insert(final SQLTable t, final String sql, final Boolean scalar) throws SQLException {
final SQLSystem sys = t.getServer().getSQLSystem();
if (scalar != null && sys == SQLSystem.H2)
throw new IllegalArgumentException("H2 use IDENTITY() which only returns the last ID: " + t.getSQLName());
if (scalar != null && sys == SQLSystem.MSSQL)
throw new IllegalArgumentException("In MS getUpdateCount() is correct but getGeneratedKeys() only returns the last ID: " + t.getSQLName());
return SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Insertion<?>, SQLException>() {
@Override
public Insertion<?> handle(SQLDataSource ds) throws SQLException {
final Statement stmt = ds.getConnection().createStatement();
try {
final ResultSet rs;
// ATTN don't call quote() with the passed sql otherwise it will try to parse %
final String insertInto = "INSERT INTO " + t.getSQLName().quote() + " " + sql;
if (sys == SQLSystem.POSTGRESQL) {
final String returning;
// no need to return something if it won't get used
if (scalar != null && t.getPrimaryKeys().size() > 0) {
returning = " RETURNING " + CollectionUtils.join(t.getPrimaryKeys(), ", ", new ITransformer<SQLField, String>() {
@Override
public String transformChecked(SQLField pk) {
return SQLBase.quoteIdentifier(pk.getName());
private static final Insertion<?> insert(final SQLTable t, final String sql, final ReturnMode mode) throws SQLException {
return new Inserter(t).insert(sql, mode, true);
}
});
} else {
returning = "";
}
stmt.execute(insertInto + returning);
// null if no returning
rs = stmt.getResultSet();
} else {
// MySQL always return an empty resultSet for anything else than 1 pk
final boolean dontGetGK = scalar == null || (sys == SQLSystem.MYSQL && t.getPrimaryKeys().size() != 1);
stmt.execute(insertInto, dontGetGK ? Statement.NO_GENERATED_KEYS : Statement.RETURN_GENERATED_KEYS);
rs = dontGetGK ? null : stmt.getGeneratedKeys();
}
final List<?> list;
final int count;
// cannot get or doesn't want the list
if (rs == null || scalar == null) {
list = null;
count = stmt.getUpdateCount();
} else {
list = (List<?>) (scalar ? SQLDataSource.COLUMN_LIST_HANDLER : SQLDataSource.ARRAY_LIST_HANDLER).handle(rs);
count = list.size();
}
@SuppressWarnings("unchecked")
final Insertion<?> res = new Insertion<Object>((List<Object>) list, count);
return res;
} finally {
stmt.close();
}
}
});
}
 
/**
* Insert rows in the passed table.
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBFileCache.java
14,12 → 14,15
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.graph.DatabaseGraph;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.StringUtils;
 
import java.io.File;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
28,6 → 31,12
/**
* A file cache for any data pertaining to a DBStructureItem. Obtained through
* {@link SQLServer#getFileCache()}.
* <p>
* Note: this class (and {@link DBItemFileCache}) use privileged blocks for read operations, but not
* for write operations (e.g. {@link #delete()}) : callers should handle it (e.g.
* {@link SQLBase#fetchTables(org.openconcerto.sql.model.graph.TablesMap)},
* {@link DatabaseGraph#refresh(org.openconcerto.sql.model.graph.TablesMap, boolean)}).
* </p>
*
* @author Sylvain
* @see #getChild(DBStructureItem)
109,8 → 118,15
throw new NullPointerException("null server");
this.server = s;
try {
FileUtils.mkdir_p(this.getSystemDir());
} catch (IOException e) {
// TODO limit to file write permission
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
FileUtils.mkdir_p(getSystemDir());
return null;
}
});
} catch (PrivilegedActionException e) {
throw new IllegalArgumentException("could not create dirs", e);
}
this.serverCache = getChildFrom(this.getSystemDir(), Collections.singletonList(this.server.getID()));
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLDataSource.java
23,6 → 23,8
import org.openconcerto.utils.ThreadFactory;
import org.openconcerto.utils.cache.CacheResult;
 
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
102,6 → 104,9
static public final int loginTimeOut = 15;
static public final int socketTimeOut = 8 * 60;
 
// in milliseconds
static public int QUERY_TUNING = 0;
 
static public interface IgnoringRowProcessor extends RowProcessor {
 
@Override
155,6 → 160,7
private SQLCache<List<?>, Object> cache;
@GuardedBy("this")
private boolean cacheEnabled;
private final PropertyChangeListener descL;
// tables that can be used in queries (and thus can impact the cache)
@GuardedBy("this")
private Set<SQLTable> tables;
161,7 → 167,7
 
private static int count = 0; // compteur de requetes
 
private final SQLServer server;
private final DBSystemRoot sysRoot;
// no need to synchronize multiple call to this attribute since we only access the
// Thread.currentThread() key
@GuardedBy("handlers")
198,14 → 204,14
 
private final ReentrantLock testLock = new ReentrantLock();
 
public SQLDataSource(SQLServer server, String base, String login, String pass) {
this(server, server.getURL(base), login, pass, Collections.<SQLTable> emptySet());
public SQLDataSource(DBSystemRoot sysRoot, String base, String login, String pass) {
this(sysRoot, sysRoot.getServer().getURL(base), login, pass, Collections.<SQLTable> emptySet());
}
 
private SQLDataSource(SQLServer server, String url, String login, String pass, Set<SQLTable> tables) {
this(server);
private SQLDataSource(DBSystemRoot sysRoot, String url, String login, String pass, Set<SQLTable> tables) {
this(sysRoot);
 
final SQLSystem system = server.getSQLSystem();
final SQLSystem system = getSystem();
if (!DRIVERS.containsKey(system))
throw new IllegalArgumentException("unknown database system: " + system);
 
216,14 → 222,12
this.setPassword(pass);
this.setTables(tables);
 
if (this.server.getSQLSystem() == SQLSystem.MYSQL) {
if (system == SQLSystem.MYSQL) {
this.addConnectionProperty("transformedBitIsBoolean", "true");
// by default allowMultiQueries, since it's more convenient (i.e. pass String around
// instead of List<String>) and faster (less trips to the server, allow
// SQLUtils.executeMultiple())
this.addConnectionProperty("allowMultiQueries", "true");
} else if (this.server.getSQLSystem() == SQLSystem.H2) {
this.addConnectionProperty("CACHE_SIZE", "32000");
}
this.setLoginTimeout(loginTimeOut);
this.setSocketTimeout(socketTimeOut);
235,19 → 239,19
 
@Override
public final void setLoginTimeout(int timeout) {
if (this.server.getSQLSystem() == SQLSystem.MYSQL) {
if (this.getSystem() == SQLSystem.MYSQL) {
this.addConnectionProperty("connectTimeout", timeout + "000");
} else if (this.server.getSQLSystem() == SQLSystem.POSTGRESQL) {
} else if (this.getSystem() == SQLSystem.POSTGRESQL) {
this.addConnectionProperty("loginTimeout", timeout + "");
}
}
 
public final void setSocketTimeout(int timeout) {
if (this.server.getSQLSystem() == SQLSystem.MYSQL) {
if (this.getSystem() == SQLSystem.MYSQL) {
this.addConnectionProperty("socketTimeout", timeout + "000");
} else if (this.server.getSQLSystem() == SQLSystem.H2) {
} else if (this.getSystem() == SQLSystem.H2) {
this.addConnectionProperty("QUERY_TIMEOUT", timeout + "000");
} else if (this.server.getSQLSystem() == SQLSystem.POSTGRESQL) {
} else if (this.getSystem() == SQLSystem.POSTGRESQL) {
this.addConnectionProperty("socketTimeout", timeout + "");
}
}
303,8 → 307,8
}
 
/* pour le clonage */
private SQLDataSource(SQLServer server) {
this.server = server;
private SQLDataSource(DBSystemRoot sysRoot) {
this.sysRoot = sysRoot;
// on a besoin d'une implementation synchronisée
this.handlers = new Hashtable<Thread, HandlersStack>();
// weak, since this is only a hint to avoid initializing the connection
344,6 → 348,16
 
// see #createDataSource() for properties not supported by this class
this.tables = Collections.emptySet();
this.descL = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("descendants")) {
// the dataSource must always have all tables, to listen to them for its cache
setTables(((DBSystemRoot) evt.getSource()).getDescs(SQLTable.class));
}
}
};
this.sysRoot.addListener(this.descL);
this.cache = null;
this.cacheEnabled = false;
}
891,7 → 905,17
State.INSTANCE.beginRequest(query);
 
// test before calling JDBC methods and creating threads
if (Thread.currentThread().isInterrupted()) {
boolean interrupted = false;
if (QUERY_TUNING > 0) {
try {
Thread.sleep(QUERY_TUNING);
} catch (InterruptedException e1) {
interrupted = true;
}
} else {
interrupted = Thread.currentThread().isInterrupted();
}
if (interrupted) {
throw new RTInterruptedException("request interrupted : " + query);
}
 
899,7 → 923,8
ResultSet rs = null;
try {
// MAYBE un truc un peu plus formel
if (query.startsWith("INSERT") || query.startsWith("UPDATE") || query.startsWith("DELETE") || query.startsWith("ALTER") || query.startsWith("DROP") || query.startsWith("SET")) {
if (query.startsWith("INSERT") || query.startsWith("UPDATE") || query.startsWith("DELETE") || query.startsWith("CREATE") || query.startsWith("ALTER") || query.startsWith("DROP")
|| query.startsWith("SET")) {
final boolean returnGenK = (query.startsWith("INSERT") || query.startsWith("UPDATE")) && stmt.getConnection().getMetaData().supportsGetGeneratedKeys();
stmt.executeUpdate(query, returnGenK ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS);
rs = returnGenK ? stmt.getGeneratedKeys() : null;
1015,6 → 1040,7
* @throws SQLException if a database error occurs
*/
public synchronized void close() throws SQLException {
this.sysRoot.rmListener(this.descL);
@SuppressWarnings("rawtypes")
final GenericObjectPool pool = this.connectionPool;
super.close();
1037,7 → 1063,7
}
 
private synchronized void noConnectionIsOpen() {
assert this.connectionPool.getNumIdle() + this.connectionPool.getNumActive() == 0;
assert this.connectionPool == null || (this.connectionPool.getNumIdle() + this.getBorrowedConnectionCount()) == 0;
if (this.cache != null)
this.cache.clear();
}
1174,7 → 1200,7
}
 
public final int getBorrowedConnectionCount() {
return this.connectionPool.getNumActive();
return this.connectionPool == null ? 0 : this.connectionPool.getNumActive();
}
 
public synchronized boolean blocksWhenExhausted() {
1501,9 → 1527,9
* default schema.
*/
public void setInitialSchema(String schemaName) {
if (schemaName != null || this.server.getSQLSystem().isClearingPathSupported()) {
if (schemaName != null || this.getSystem().isClearingPathSupported()) {
this.setInitialSchema(true, schemaName);
} else if (this.server.getSQLSystem().isDBPathEmpty()) {
} else if (this.getSystem().isDBPathEmpty()) {
this.unsetInitialSchema();
} else
throw new IllegalArgumentException(this + " cannot have no default schema");
1636,11 → 1662,11
}
 
private final SQLSystem getSystem() {
return this.server.getSQLSystem();
return this.sysRoot.getServer().getSQLSystem();
}
 
public Object clone() {
SQLDataSource ds = new SQLDataSource(this.server);
SQLDataSource ds = new SQLDataSource(this.sysRoot);
ds.setUrl(this.getUrl());
ds.setUsername(this.getUsername());
ds.setPassword(this.getPassword());
/trunk/OpenConcerto/src/org/openconcerto/sql/model/StructureSource.java
17,6 → 17,9
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.cc.IncludeExclude;
 
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
107,11 → 110,22
try {
this.getBase().getDataSource().useConnection(new ConnectionHandlerNoSetup<Object, E>() {
@Override
public Object handle(SQLDataSource ds) throws E {
public Object handle(final SQLDataSource ds) throws E {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws E {
getNames(ds.getConnection());
return null;
}
});
} catch (PrivilegedActionException e) {
@SuppressWarnings("unchecked")
final E origExn = (E) e.getCause();
throw origExn;
}
}
});
if (this.isPreVerify()) {
this.newStructure = this.createTables();
this.fillTablesP();
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/GraFFF.java
113,7 → 113,7
* @return un ensemble de Path.
*/
public Set<Path> findAllPath(SQLTable from, SQLTable to) {
return this.findAllPath(from, to, new Path(from));
return this.findAllPath(from, to, Path.get(from));
}
 
private Set<Path> findAllPath(SQLTable from, SQLTable to, Path been) {
128,8 → 128,7
// on essaye si y on pas déjà été
if (!been.getTables().contains(neighbour)) {
// on avance d'un cran
Path newBeen = new Path(been);
newBeen.add(l);
Path newBeen = been.add(l);
res.addAll(this.findAllPath(neighbour, to, newBeen));
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/Step.java
132,6 → 132,14
return new List2<SQLTable>(l.getTarget(), l.getSource());
}
 
/**
* Create a new step with the passed links.
*
* @param links the links to cross, direction cannot be {@link Direction#ANY}.
* @return a new step.
* @throws IllegalArgumentException if <code>links</code> is empty, if {@link Direction#ANY} is
* present, or if not all links share end points.
*/
public static final Step create(final Map<Link, Direction> links) {
List2<SQLTable> startEnd = null;
for (final Entry<Link, Direction> e : links.entrySet()) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/Path.java
21,46 → 21,27
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
import net.jcip.annotations.Immutable;
 
/**
* Un chemin dans une base de donnée. Un chemin est composé de tables, d'autre part les tables sont
* relées entre elle par un sous-ensemble des champs les reliant dans le graphe.
* reliées entre elle par un sous-ensemble des champs les reliant dans le graphe. This class is
* immutable and thus thread-safe. Also methods that modify a path always return a new instance. If
* a few modifications are needed consider using {@link #toBuilder()}.
*
* @author ILM Informatique 4 oct. 2004
*/
public class Path {
@Immutable
public final class Path extends AbstractPath<Path> {
 
/**
* Crée un chemin à partir d'une liste de String.
*
* @param base la base du chemin.
* @param path le chemin sous forme de String
* @return le chemin correspondant.
* @deprecated use {@link #createFromTables(DBRoot, List)} instead or the other methods with
* String parameters (see {@link #add(String)}
*/
static Path create(DBRoot base, List<String> path) {
if (path.size() == 0)
throw new IllegalArgumentException("path is empty");
 
// le premier doit etre une table car un champ est ambigu.
final SQLTable first = base.getTable(path.get(0));
if (first == null)
throw new IllegalArgumentException("first item must be a table");
Path res = new Path(first);
for (int i = 1; i < path.size(); i++) {
res.add(path.get(i));
}
 
return res;
}
 
/**
* Create a path from tables.
*
* @param root the root of the first table.
70,9 → 51,30
*/
static public Path createFromTables(DBRoot root, List<String> path) {
final SQLTable first = root.getTable(path.get(0));
return new Path(first).addTables(path.subList(1, path.size()));
return new PathBuilder(first).addTables(path.subList(1, path.size())).build();
}
 
private static final int CACHE_INITIAL_CAPACITY = 8;
private static final float CACHE_LOAD_FACTOR = 0.75f;
private static final int CACHE_MAX_SIZE = (int) (CACHE_LOAD_FACTOR * CACHE_INITIAL_CAPACITY * 8);
static private final Map<SQLTable, Path> EMPTY_PATHS = new LinkedHashMap<SQLTable, Path>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR, true) {
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<SQLTable, Path> eldest) {
return size() > CACHE_MAX_SIZE;
}
};
 
static public Path get(SQLTable first) {
synchronized (EMPTY_PATHS) {
Path res = EMPTY_PATHS.get(first);
if (res == null) {
res = new Path(first);
EMPTY_PATHS.put(first, res);
}
return res;
}
}
 
/**
* Crée un chemin à partir d'une liste de Link.
*
84,14 → 86,21
// besoin de spécifier le premier, eg BATIMENT.ID_SITE => quel sens ?
if (links == null)
return null;
final Path res = new Path(first);
for (final Link link : links) {
res.add(link);
final PathBuilder builder = new PathBuilder(first);
for (final Link l : links)
builder.add(l);
return builder.build();
}
return res;
 
static private final <T> List<T> copy(List<T> l) {
return Collections.unmodifiableList(new ArrayList<T>(l));
}
 
private final DBRoot base;
// arguments not checked for coherence
static Path create(final List<SQLTable> tables, final List<Step> steps, final List<SQLField> singleFields) {
return new Path(copy(tables), copy(steps), copy(singleFields));
}
 
private final List<SQLTable> tables;
private final List<Step> fields;
// after profiling: doing getStep().iterator().next() costs a lot
98,55 → 107,46
private final List<SQLField> singleFields;
 
public Path(SQLTable start) {
this.tables = new ArrayList<SQLTable>();
this.fields = new ArrayList<Step>();
this.singleFields = new ArrayList<SQLField>();
this.base = start.getDBRoot();
this.tables.add(start);
this.tables = Collections.singletonList(start);
this.fields = Collections.emptyList();
this.singleFields = Collections.emptyList();
}
 
public Path(Path p) {
this.tables = new ArrayList<SQLTable>(p.tables);
// ok since Step is immutable
this.fields = new ArrayList<Step>(p.fields);
this.singleFields = new ArrayList<SQLField>(p.singleFields);
this.base = p.base;
public Path(Step step) {
this.tables = Arrays.asList(step.getFrom(), step.getTo());
this.fields = Collections.singletonList(step);
this.singleFields = Collections.singletonList(step.getSingleField());
}
 
public final Path reverse() {
final Path res = new Path(getLast());
for (int i = this.fields.size() - 1; i >= 0; i--) {
res.add(this.fields.get(i).reverse());
// arguments not checked for coherence, nor immutability
private Path(final List<SQLTable> tables, final List<Step> steps, final List<SQLField> singleFields) {
this.tables = tables;
this.fields = steps;
this.singleFields = singleFields;
}
return res;
}
 
/**
* La longueur de ce chemin.
*
* @return la longueur de ce chemin.
*/
public int length() {
return this.fields.size();
public final PathBuilder toBuilder() {
return new PathBuilder(this);
}
 
public SQLTable getFirst() {
return this.getTable(0);
}
public final Path reverse() {
final int stepsCount = this.fields.size();
if (stepsCount == 0)
return this;
 
public SQLTable getLast() {
return this.getTable(this.tables.size() - 1);
}
final List<SQLTable> tables = new ArrayList<SQLTable>(stepsCount + 1);
final List<Step> steps = new ArrayList<Step>(stepsCount);
final List<SQLField> singleFields = new ArrayList<SQLField>(stepsCount);
 
/**
* La table se trouvant à la position demandée dans ce chemin.
*
* @param i l'index, entre 0 et length() inclus.
* @return la table se trouvant à la position demandée.
*/
public SQLTable getTable(int i) {
return this.tables.get(i);
for (int i = stepsCount - 1; i >= 0; i--) {
final Step reversedStep = this.fields.get(i).reverse();
tables.add(reversedStep.getFrom());
steps.add(reversedStep);
singleFields.add(reversedStep.getSingleField());
}
tables.add(this.getFirst());
return new Path(Collections.unmodifiableList(tables), Collections.unmodifiableList(steps), Collections.unmodifiableList(singleFields));
}
 
public Path minusFirst() {
return this.subPath(1, this.length());
177,23 → 177,18
public Path subPath(int fromIndex, int toIndex) {
fromIndex = CollectionUtils.getValidIndex(this.fields, fromIndex);
toIndex = CollectionUtils.getValidIndex(this.fields, toIndex);
final Path p = new Path(this.getTable(fromIndex));
// +1 since we've passed the first to the ctor
// +1 since there's one more table than fields
p.tables.addAll(this.tables.subList(fromIndex + 1, toIndex + 1));
p.fields.addAll(this.fields.subList(fromIndex, toIndex));
p.singleFields.addAll(this.singleFields.subList(fromIndex, toIndex));
return p;
if (fromIndex == 0 && toIndex == this.length())
return this;
return new Path(this.tables.subList(fromIndex, toIndex + 1), this.fields.subList(fromIndex, toIndex), this.singleFields.subList(fromIndex, toIndex));
}
 
public Path justFirst() {
final Path p = new Path(this.getFirst());
p.add(this.fields.get(0));
return p;
return subPath(0, 1);
}
 
@Override
public List<SQLTable> getTables() {
return Collections.unmodifiableList(this.tables);
return this.tables;
}
 
/**
200,233 → 195,62
* Append <code>p</code> to this path.
*
* @param p the path to append.
* @return this.
* @return a new path.
* @throws IllegalArgumentException if <code>p</code> doesn't begin where this ends.
*/
public final Path append(Path p) {
if (this.getLast() != p.getFirst())
throw new IllegalArgumentException("this ends at " + this.getLast() + " while the other begins at " + p.getFirst());
this.fields.addAll(p.fields);
this.singleFields.addAll(p.singleFields);
this.tables.addAll(p.tables.subList(1, p.tables.size()));
@Override
protected final Path _append(Path p) {
final int thisLength = this.length();
final int oLength = p.length();
if (thisLength == 0)
return p;
else if (oLength == 0)
return this;
}
 
private Path add(Step step) {
assert step.getFrom() == this.getLast() : "broken path";
this.fields.add(step);
this.singleFields.add(step.getSingleField());
this.tables.add(step.getTo());
return this;
}
final int stepsCount = thisLength + oLength;
final List<SQLTable> tables = new ArrayList<SQLTable>(stepsCount + 1);
final List<Step> steps = new ArrayList<Step>(stepsCount);
final List<SQLField> singleFields = new ArrayList<SQLField>(stepsCount);
 
/**
* Ajoute un maillon a la chaine. item doit être soit le nom d'une table, soit le nom complet
* d'un champ (TABLE.FIELD_NAME).
*
* @param item le nouveau maillon.
* @return this.
* @deprecated use {@link #addForeignField(String)}, {@link #addForeignTable(String)} or similar
*/
private Path add(String item) {
int dot = item.indexOf('.');
if (dot < 0) {
return add(this.base.getTable(item));
} else {
return add(this.base.getDesc(item, SQLField.class));
}
}
// subList() since the last table will be added from p
tables.addAll(this.tables.subList(0, thisLength));
tables.addAll(p.tables);
steps.addAll(this.fields);
steps.addAll(p.fields);
singleFields.addAll(this.singleFields);
singleFields.addAll(p.singleFields);
 
/**
* Add a table at the end of the path. NOTE: the step will be composed of all the foreign fields
* between {@link #getLast()} and <code>destTable</code>.
*
* @param destTable the table to add.
* @return this.
* @throws IllegalArgumentException if <code>destTable</code> has no foreign fields between
* itself and the current end of this path.
*/
public final Path add(final SQLTable destTable) {
return this.add(destTable, Direction.ANY);
return new Path(Collections.unmodifiableList(tables), Collections.unmodifiableList(steps), Collections.unmodifiableList(singleFields));
}
 
public final Path add(final SQLTable destTable, final Direction dir) {
return this.add(Step.create(getLast(), destTable, dir));
@Override
final Path add(Step step) {
return this.append(new Path(step));
}
 
/**
* Add a table at the end of the path. NOTE: the step will be composed of all the foreign fields
* between {@link #getLast()} and <code>tableName</code>.
*
* @param tableName the table name.
* @return this.
*/
public final Path addTable(final String tableName) {
return this.addTable(tableName, Direction.ANY, false);
@Override
public final List<Step> getSteps() {
return this.fields;
}
 
public final Path addTable(final String tableName, final Direction dir, final boolean onlyOne) {
return this.add(dir, null, tableName, null, onlyOne);
}
 
public Path addTables(String... names) {
return this.addTables(Arrays.asList(names));
}
 
@Override
public Path addTables(List<String> names) {
for (final String name : names)
this.addTable(name);
return this;
return toBuilder().addTables(names).build();
}
 
public final Path addForeignTable(final String tableName) {
return this.addForeignTable(tableName, null);
}
 
/**
* Add a table at the end of the path if there's only one <b>foreign<b> link between the end and
* it.
*
* @param tableName the table name.
* @param rootName the name of the table root, <code>null</code> to not use.
* @return this.
*/
public final Path addForeignTable(final String tableName, final String rootName) {
return this.add(Direction.FOREIGN, null, tableName, rootName, true);
}
 
/**
* Add a step to the path.
*
* @param fField a foreign field.
* @return this.
* @throws IllegalArgumentException if <code>fField</code> is not a foreign field or if neither
* of its ends are the current end of this path.
*/
public final Path add(final SQLField fField) {
return this.add(fField, Direction.ANY);
}
 
/**
* Add a step to the path.
*
* @param fField a foreign field.
* @param direction <code>true</code> to cross from the source of <code>fField</code> to its
* destination, <code>null</code> to infer it.
* @return this.
* @throws IllegalArgumentException if <code>fField</code> is not a foreign field or if neither
* of its ends are the current end of this path.
* @deprecated use {@link #add(SQLField, Direction)}
*/
public final Path add(final SQLField fField, final Boolean direction) {
return this.add(fField, Direction.fromForeign(direction));
}
 
public final Path add(final SQLField fField, final Direction direction) {
return this.add(Step.create(getLast(), fField, direction));
}
 
/**
* Add a step to the path.
*
* @param fields fields between the last table and another.
* @return this.
*/
public final Path addStepWithFields(final Collection<SQLField> fields) {
final Set<Link> links = new HashSet<Link>(fields.size());
final DatabaseGraph graph = getFirst().getDBSystemRoot().getGraph();
for (final SQLField f : fields) {
links.add(graph.getForeignLink(f));
}
return this.add(links);
}
 
/**
* Add multiple steps to the path.
*
* @param fieldsNames foreign fields.
* @return this.
* @return a new path.
* @see #addForeignField(String)
*/
@Override
public final Path addForeignFields(final String... fieldsNames) {
for (final String name : fieldsNames)
this.addForeignField(name);
return this;
return toBuilder().addForeignFields(fieldsNames).build();
}
 
public final Path addForeignField(final String fieldName) {
return this.add(this.getLast().getField(fieldName));
}
 
public final Path addReferentField(final String fieldName) {
return this.addReferent(fieldName, null, null);
}
 
public final Path addReferentTable(final String tableName) {
return this.addReferent(null, tableName, null);
}
 
/**
* Add a table at the end of this path if there's only one link matching the parameters.
*
* @param fieldName the field name, <code>null</code> to not use.
* @param tableName the name of the added table, <code>null</code> to not use.
* @param rootName the name of the root of the added table, <code>null</code> to not use.
* @return this.
*/
public final Path addReferent(final String fieldName, final String tableName, final String rootName) {
return this.add(Direction.REFERENT, fieldName, tableName, rootName, true);
}
 
/**
* Add a step to the path.
*
* @param dir the direction of the new step.
* @param fieldName the field name, <code>null</code> to not use.
* @param tableName the name of the added table, <code>null</code> to not use.
* @param rootName the name of the root of the added table, <code>null</code> to not use.
* @param onlyOne <code>true</code> if one and only one link should match.
* @return this.
* @throws IllegalStateException if <code>onlyOne</code> is <code>true</code> and not one and
* only one link matching.
*/
public final Path add(final Direction dir, final String fieldName, final String tableName, final String rootName, final boolean onlyOne) {
return this.add(this.getLast().getDBSystemRoot().getGraph().getLinks(getLast(), dir, onlyOne, new Link.NamePredicate(getLast(), rootName, tableName, fieldName)));
}
 
/**
* Add a step to the path.
*
* @param links links from the current end of this path to another table.
* @return this.
* @throws IllegalArgumentException if <code>links</code> are not all between the current end
* and the same other table.
*/
public final Path add(final Collection<Link> links) {
return this.add(Step.create(getLast(), links));
}
 
/**
* Ajoute un maillon a la chaine.
*
* @param item un lien.
* @return this.
* @throws IllegalArgumentException si aucune des extremités de item n'est connectée à ce
* chemin.
*/
public Path add(Link item) {
return this.add(item, Direction.ANY);
}
 
public Path add(Link l, Direction direction) {
return this.add(Step.create(getLast(), l, direction));
}
 
public final List<Step> getSteps() {
return Collections.unmodifiableList(this.fields);
}
 
/**
* The step connecting the table i to i+1.
*
* @param i the step asked, from 0 to {@link #length()} -1 or negative (the last step being -1).
466,7 → 290,7
* @see #getSingleStep(int)
*/
public final List<SQLField> getSingleSteps() {
return Collections.unmodifiableList(this.singleFields);
return this.singleFields;
}
 
/**
484,12 → 308,12
 
public final Set<Path> getSingleLinkPaths() {
if (this.length() == 0)
return Collections.singleton(new Path(this));
return Collections.singleton(this);
 
final Set<Path> res = new HashSet<Path>();
for (final Path p : this.subPath(1).getSingleLinkPaths()) {
for (final Step s : this.getStep(0).getSingleSteps()) {
res.add(new Path(this.getFirst()).add(s).append(p));
res.add(new PathBuilder(s).append(p).build());
}
}
return res;
585,7 → 409,7
final Path o = (Path) obj;
// no need to compare tables, starting from the same point the same fields lead to the
// same table
return this.base.equals(o.base) && this.getFirst().equals(o.getFirst()) && this.fields.equals(o.fields);
return this.getFirst().equals(o.getFirst()) && this.fields.equals(o.fields);
} else
return false;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/AbstractPath.java
New file
0,0 → 1,251
/*
* 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.model.graph;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.utils.CollectionUtils;
 
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
 
abstract class AbstractPath<T extends AbstractPath<T>> {
 
public final SQLTable getFirst() {
return this.getTable(0);
}
 
public final SQLTable getLast() {
return this.getTable(this.getTables().size() - 1);
}
 
/**
* La table se trouvant à la position demandée dans ce chemin.
*
* @param i l'index, entre 0 et length() inclus.
* @return la table se trouvant à la position demandée.
*/
public final SQLTable getTable(int i) {
return this.getTables().get(i);
}
 
public abstract List<SQLTable> getTables();
 
public final int length() {
return this.getSteps().size();
}
 
public final T append(Path p) {
if (this.getLast() != p.getFirst())
throw new IllegalArgumentException("this ends at " + this.getLast() + " while the other begins at " + p.getFirst());
return this._append(p);
}
 
protected abstract T _append(Path p);
 
abstract T add(Step step);
 
/**
* Add a table at the end of the path. NOTE: the step will be composed of all the foreign fields
* between {@link #getLast()} and <code>destTable</code>.
*
* @param destTable the table to add.
* @return an instance with the table added.
* @throws IllegalArgumentException if <code>destTable</code> has no foreign fields between
* itself and the current end of this path.
*/
public final T add(final SQLTable destTable) {
return this.add(destTable, Direction.ANY);
}
 
public final T add(final SQLTable destTable, final Direction dir) {
return this.add(Step.create(getLast(), destTable, dir));
}
 
/**
* Add a table at the end of the path. NOTE: the step will be composed of all the foreign fields
* between {@link #getLast()} and <code>tableName</code>.
*
* @param tableName the table name.
* @return an instance with the table added.
*/
public final T addTable(final String tableName) {
return this.addTable(tableName, Direction.ANY, false);
}
 
public final T addTable(final String tableName, final Direction dir, final boolean onlyOne) {
return this.add(dir, null, tableName, null, onlyOne);
}
 
public final T addTables(String... names) {
return this.addTables(Arrays.asList(names));
}
 
public abstract T addTables(List<String> names);
 
public final T addForeignTable(final String tableName) {
return this.addForeignTable(tableName, null);
}
 
/**
* Add a table at the end of the path if there's only one <b>foreign<b> link between the end and
* it.
*
* @param tableName the table name.
* @param rootName the name of the table root, <code>null</code> to not use.
* @return an instance with the table added.
*/
public final T addForeignTable(final String tableName, final String rootName) {
return this.add(Direction.FOREIGN, null, tableName, rootName, true);
}
 
/**
* Add a step to the path.
*
* @param fField a foreign field.
* @return an instance with the field added.
* @throws IllegalArgumentException if <code>fField</code> is not a foreign field or if neither
* of its ends are the current end of this path.
*/
public final T add(final SQLField fField) {
return this.add(fField, Direction.ANY);
}
 
/**
* Add a step to the path.
*
* @param fField a foreign field.
* @param direction how to cross <code>fField</code>, <code>ANY</code> to infer it.
* @return an instance with the field added.
* @throws IllegalArgumentException if <code>fField</code> is not a foreign field or if neither
* of its ends are the current end of this path, or if <code>direction</code> isn't
* valid.
* @see Step#create(SQLTable, SQLField, Direction)
*/
public final T add(final SQLField fField, final Direction direction) {
return this.add(Step.create(getLast(), fField, direction));
}
 
/**
* Add a step to the path.
*
* @param fields fields between the last table and another.
* @return an instance with the step added.
*/
public final T addStepWithFields(final Collection<SQLField> fields) {
final Set<Link> links = new HashSet<Link>(fields.size());
final DatabaseGraph graph = getFirst().getDBSystemRoot().getGraph();
for (final SQLField f : fields) {
links.add(graph.getForeignLink(f));
}
return this.add(links);
}
 
/**
* Add multiple steps to the path.
*
* @param fieldsNames foreign fields.
* @return an instance with the steps added.
* @see #addForeignField(String)
*/
public abstract T addForeignFields(final String... fieldsNames);
 
public final T addForeignField(final String fieldName) {
return this.add(Direction.FOREIGN, fieldName, null, null, true);
}
 
public final T addReferentField(final String fieldName) {
return this.addReferent(fieldName, null, null);
}
 
public final T addReferentTable(final String tableName) {
return this.addReferent(null, tableName, null);
}
 
/**
* Add a table at the end of this path if there's only one link matching the parameters.
*
* @param fieldName the field name, <code>null</code> to not use.
* @param tableName the name of the added table, <code>null</code> to not use.
* @param rootName the name of the root of the added table, <code>null</code> to not use.
* @return an instance with the table added.
*/
public final T addReferent(final String fieldName, final String tableName, final String rootName) {
return this.add(Direction.REFERENT, fieldName, tableName, rootName, true);
}
 
/**
* Add a step to the path.
*
* @param dir the direction of the new step.
* @param fieldName the field name, <code>null</code> to not use.
* @param tableName the name of the added table, <code>null</code> to not use.
* @param rootName the name of the root of the added table, <code>null</code> to not use.
* @param onlyOne <code>true</code> if one and only one link should match.
* @return an instance with the step added.
* @throws IllegalStateException if <code>onlyOne</code> is <code>true</code> and not one and
* only one link matching.
*/
public final T add(final Direction dir, final String fieldName, final String tableName, final String rootName, final boolean onlyOne) {
final Set<Link> links = this.getLast().getDBSystemRoot().getGraph().getLinks(getLast(), dir, onlyOne, new Link.NamePredicate(getLast(), rootName, tableName, fieldName));
return this.add(links, dir);
}
 
/**
* Add a step to the path.
*
* @param links links from the current end of this path to another table.
* @return an instance with the step added.
* @throws IllegalArgumentException if <code>links</code> are not all between the current end
* and the same other table.
*/
public final T add(final Collection<Link> links) {
return this.add(links, Direction.ANY);
}
 
public final T add(final Collection<Link> links, Direction dir) {
final Step step;
if (dir == Direction.ANY) {
step = Step.create(getLast(), links);
} else {
step = Step.create(CollectionUtils.fillMap(new HashMap<Link, Direction>(), links, dir));
if (step.getFrom() != getLast())
throw new IllegalArgumentException("links from " + step.getFrom() + " not " + getLast());
}
return this.add(step);
}
 
/**
* Ajoute un maillon a la chaine.
*
* @param item un lien.
* @return an instance with the step added.
* @throws IllegalArgumentException si aucune des extremités de item n'est connectée à ce
* chemin.
*/
public final T add(Link item) {
return this.add(item, Direction.ANY);
}
 
public final T add(Link l, Direction direction) {
return this.add(Step.create(getLast(), l, direction));
}
 
public abstract List<Step> getSteps();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/PathBuilder.java
New file
0,0 → 1,111
/*
* 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.model.graph;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLTable;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
/**
* Allow to build efficiently a {@link Path}. This class is not thread-safe but allow to apply
* modifications to a path without creating a new instance for each one of them.
*
* @author Sylvain
* @see #build()
*/
public class PathBuilder extends AbstractPath<PathBuilder> {
 
private final List<SQLTable> tables;
private final List<Step> fields;
private final List<SQLField> singleFields;
 
public PathBuilder(SQLTable start) {
this.tables = new ArrayList<SQLTable>();
this.fields = new ArrayList<Step>();
this.singleFields = new ArrayList<SQLField>();
this.tables.add(start);
}
 
public PathBuilder(Step step) {
this.tables = new ArrayList<SQLTable>();
this.tables.add(step.getFrom());
this.tables.add(step.getTo());
this.fields = new ArrayList<Step>();
this.fields.add(step);
this.singleFields = new ArrayList<SQLField>();
this.singleFields.add(step.getSingleField());
}
 
public PathBuilder(Path p) {
this.tables = new ArrayList<SQLTable>(p.getTables());
// ok since Step is immutable
this.fields = new ArrayList<Step>(p.getSteps());
this.singleFields = new ArrayList<SQLField>(p.getSingleSteps());
}
 
public final Path build() {
return Path.create(this.tables, this.fields, this.singleFields);
}
 
@Override
public List<SQLTable> getTables() {
return Collections.unmodifiableList(this.tables);
}
 
@Override
protected final PathBuilder _append(final Path p) {
this.fields.addAll(p.getSteps());
this.singleFields.addAll(p.getSingleSteps());
this.tables.addAll(p.getTables().subList(1, p.getTables().size()));
return this;
}
 
@Override
final PathBuilder add(Step step) {
assert step.getFrom() == this.getLast() : "broken path";
this.fields.add(step);
this.singleFields.add(step.getSingleField());
this.tables.add(step.getTo());
return this;
}
 
@Override
public PathBuilder addTables(List<String> names) {
PathBuilder res = this;
for (final String name : names)
res = res.addTable(name);
return res;
}
 
@Override
public PathBuilder addForeignFields(String... fieldsNames) {
PathBuilder res = this;
for (final String name : fieldsNames)
res = res.addForeignField(name);
return res;
}
 
@Override
public final List<Step> getSteps() {
return Collections.unmodifiableList(this.fields);
}
 
@Override
public String toString() {
return getClass().getSimpleName() + this.tables + "\n\tLinks:" + this.fields;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/DatabaseGraph.java
46,6 → 46,9
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
166,7 → 169,15
synchronized (this) {
final GraphLoadingEvent evt = new GraphLoadingEvent(this.base).fireEvent();
try {
this.mappedFromFile = Collections.unmodifiableMap(this.mapTables(toRefresh));
// TODO limit to file permission
this.mappedFromFile = Collections.unmodifiableMap(AccessController.doPrivileged(new PrivilegedExceptionAction<Map<String, Set<String>>>() {
@Override
public Map<String, Set<String>> run() throws SQLException {
return mapTables(toRefresh);
}
}));
} catch (PrivilegedActionException e) {
throw (SQLException) e.getCause();
} finally {
evt.fireFinishingEvent();
}
371,7 → 382,6
// either we refresh the whole root and we must know which tables to use
// or we refresh only one table and tableNames is useless
assert tableName == null ^ tableNames == null;
final SQLServer server = r.getServer();
final CollectionMap<String, String> metadataFKs = new CollectionMap<String, String>(new HashSet<String>());
final List importedKeys = this.base.getDataSource().useConnection(new ConnectionHandlerNoSetup<List, SQLException>() {
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLBase.java
32,6 → 32,8
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
271,19 → 273,25
final Set<String> newSchemas = src.getTotalSchemas();
final Set<String> currentSchemas = src.getExistingSchemasToRefresh();
mustContain(this, newSchemas, currentSchemas, "schemas");
final CollectionChangeEventCreator c = this.createChildrenCreator();
// remove all schemas that are not there anymore
for (final String schema : CollectionUtils.substract(currentSchemas, newSchemas)) {
final CollectionChangeEventCreator c = this.createChildrenCreator();
this.schemas.remove(schema).dropped();
this.fireChildrenChanged(c);
}
// delete the saved schemas that we could have fetched, but haven't
// (schemas that are not in scope are simply ignored, NOT deleted)
for (final DBItemFileCache savedSchema : this.getSavedCaches(false)) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
for (final DBItemFileCache savedSchema : getSavedCaches(false)) {
if (src.isInTotalScope(savedSchema.getName()) && !newSchemas.contains(savedSchema.getName())) {
savedSchema.delete();
}
}
return null;
}
});
 
// clearNonPersistent (will be recreated by fillTables())
for (final String schema : CollectionUtils.inter(currentSchemas, newSchemas)) {
this.getSchema(schema).clearNonPersistent();
317,6 → 325,7
// fill with columns
src.fillTables();
 
this.fireChildrenChanged(c);
// don't signal our systemRoot if our server doesn't yet reference us,
// otherwise the server will create another instance and enter an infinite loop
assert this.getServer().getBase(this.getName()) == this;
332,7 → 341,7
byRoot = toRefresh;
}
}
this.getDBSystemRoot().descendantsChanged(byRoot, src.hasExternalStruct(), true);
this.getDBSystemRoot().descendantsChanged(byRoot, src.hasExternalStruct());
}
src.save();
return src;
490,9 → 499,7
SQLSchema res = this.getSchema(name);
if (res == null) {
res = new SQLSchema(this, name);
final CollectionChangeEventCreator c = this.createChildrenCreator();
this.schemas.put(name, res);
this.fireChildrenChanged(c);
}
return res;
}
699,15 → 706,18
}
}
 
boolean save(String schemaName) {
boolean save(final String schemaName) {
final DBItemFileCache schemaFileCache = this.getSchemaFileCache(schemaName);
if (schemaFileCache == null) {
return false;
} else {
final File schemaFile = schemaFileCache.getFile(FILENAME);
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
Writer pWriter = null;
try {
final String schema = this.getSchema(schemaName).toXML();
final String schema = getSchema(schemaName).toXML();
if (schema == null)
return false;
FileUtils.mkdir_p(schemaFile.getParentFile());
731,7 → 741,9
}
}
}
});
}
}
 
// *** quoting
 
778,7 → 790,7
else
replacement = quoteIdentifier(param.toString());
} else {
final SQLIdentifier ident = (SQLIdentifier) ((DBStructureItem) param).getJDBC();
final SQLIdentifier ident = (SQLIdentifier) ((DBStructureItem<?>) param).getJDBC();
if (modifier == 'f') {
replacement = ident.getSQLName().quote();
} else if (modifier == 'n')
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBSystemRoot.java
66,6 → 66,8
// linked to schemaPath and incoherentPath
@GuardedBy("this")
private SQLDataSource ds;
@GuardedBy("this")
private IClosure<? super SQLDataSource> dsInit;
// immutable
@GuardedBy("this")
private List<String> schemaPath;
83,6 → 85,7
mapNoRoots();
this.useCache = Boolean.getBoolean(SQLBase.STRUCTURE_USE_XML);
this.ds = null;
this.dsInit = null;
this.schemaPath = Collections.emptyList();
this.incoherentPath = false;
 
160,6 → 163,14
this.setRootsToMap(rootsNames, true, false);
}
 
public final boolean isMappingAllRoots() {
return this.getRootsToMap() == null;
}
 
public final boolean isMappingNoRoots() {
return Collections.emptySet().equals(this.getRootsToMap());
}
 
/**
* The roots that will be mapped.
*
332,10 → 343,10
}
 
void descendantsChanged(DBStructureItemJDBC parent, Set<String> childrenRefreshed, final boolean readCache) {
this.descendantsChanged(TablesMap.createByRootFromChildren(parent, childrenRefreshed), readCache, true);
this.descendantsChanged(TablesMap.createByRootFromChildren(parent, childrenRefreshed), readCache);
}
 
void descendantsChanged(TablesMap tablesRefreshed, final boolean readCache, final boolean tableListChange) {
void descendantsChanged(TablesMap tablesRefreshed, final boolean readCache) {
assert Thread.holdsLock(getTreeMutex()) : "By definition descendants must be changed with the tree lock";
try {
// don't fire GraphLoadingEvent here since we might be in an atomicRefresh
343,10 → 354,10
} catch (SQLException e) {
throw new IllegalStateException("Couldn't refresh the graph", e);
}
// the dataSource must always have all tables, to listen to them for its cache
if (tableListChange)
this.getDataSource().setTables(getDescs(SQLTable.class));
synchronized (this.supp) {
this.supp.firePropertyChange("descendants", null, null);
}
}
 
public DatabaseGraph getGraph() {
synchronized (this.graphMutex) {
554,16 → 565,31
return res;
}
 
synchronized final void setDS(String login, String pass, IClosure<SQLDataSource> dsInit) {
if (this.ds != null)
throw new IllegalStateException("already set: " + this.ds);
public synchronized final SQLDataSource createDS(String login, String pass) {
if (this.ds == null)
throw new IllegalStateException("primary DS not set");
return createDSUnsafe(login, pass);
}
 
// don't check this.ds
private synchronized final SQLDataSource createDSUnsafe(String login, String pass) {
this.checkDropped();
// either base or above
final String baseName = this.getLevel() == HierarchyLevel.SQLBASE ? this.getName() : "";
this.ds = new SQLDataSource(this.getServer(), baseName, login, pass);
if (dsInit != null)
dsInit.executeChecked(this.ds);
final SQLDataSource res = new SQLDataSource(this, baseName, login, pass);
if (this.dsInit != null)
this.dsInit.executeChecked(res);
 
return res;
}
 
synchronized final void setDS(String login, String pass, IClosure<? super SQLDataSource> dsInit) {
if (this.ds != null)
throw new IllegalStateException("already set: " + this.ds);
assert this.dsInit == null;
this.dsInit = dsInit;
this.ds = createDSUnsafe(login, pass);
 
synchronized (this.graphMutex) {
this.graph = new DatabaseGraph(this);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/XMLStructureSource.java
43,7 → 43,7
* Date format used in xml files.
*/
public static final DateFormat XMLDATE_FMT = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ");
public static final String version = "20121123-1059";
public static final String version = "20140213-1231";
 
private final Map<String, Element> xmlSchemas;
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSystem.java
41,7 → 41,7
*
* @see <a href="http://www.postgresql.org/">PostgreSQL site</a>
*/
POSTGRESQL {
POSTGRESQL("PostgreSQL") {
@Override
void removeRootsToIgnore(Set<String> s) {
super.removeRootsToIgnore(s);
67,6 → 67,11
public boolean isIndexFilterConditionSupported() {
return true;
}
 
@Override
public boolean isSequencesSupported() {
return true;
}
},
 
/**
80,7 → 85,7
*
* @see <a href="http://www.mysql.com/">MySQL site</a>
*/
MYSQL {
MYSQL("MySQL") {
@Override
EnumSet<HierarchyLevel> createLevels() {
// mysql has no schema
127,7 → 132,7
*
* @see <a href="http://www.h2database.com/">H2 site</a>
*/
H2 {
H2("H2") {
 
private static final String TCP_PREFIX = "tcp://";
private static final String SSL_PREFIX = "ssl://";
169,6 → 174,11
}
 
@Override
public boolean isSequencesSupported() {
return true;
}
 
@Override
public String getServerName(final String host) {
return TCP_PREFIX + host + "/";
}
243,7 → 253,7
}
 
},
MSSQL {
MSSQL("Microsoft SQL Server") {
@Override
public String getJDBCName() {
return "sqlserver";
275,7 → 285,7
return false;
}
},
DERBY;
DERBY("Apache Derby");
 
public static SQLSystem get(String name) {
final String normalized = name.toUpperCase();
290,9 → 300,11
}
}
 
private final String label;
private final EnumOrderedSet<HierarchyLevel> levels;
 
{
private SQLSystem(final String label) {
this.label = label;
this.levels = new EnumOrderedSet<HierarchyLevel>(this.createLevels());
}
 
305,6 → 317,10
return this.name().toLowerCase();
}
 
public final String getLabel() {
return this.label;
}
 
EnumSet<HierarchyLevel> createLevels() {
return EnumSet.allOf(HierarchyLevel.class);
}
470,6 → 486,10
return true;
}
 
public boolean isSequencesSupported() {
return false;
}
 
public String getMDName(String name) {
return name;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTable.java
102,7 → 102,7
if (schema.contains(undefTable)) {
final SQLBase b = schema.getBase();
final SQLTable undefT = schema.getTable(undefTable);
final SQLSelect sel = new SQLSelect(b).addSelectStar(undefT);
final SQLSelect sel = new SQLSelect().addSelectStar(undefT);
r = (Map<String, Number>) b.getDataSource().execute(sel.asString(), new ResultSetHandler() {
public Object handle(ResultSet rs) throws SQLException {
final Map<String, Number> res = new HashMap<String, Number>();
470,8 → 470,7
return n.equals(this.getName()) && CompareUtils.equals(s, this.getSchema().getName());
}
 
@SuppressWarnings("unchecked")
void addTrigger(Map m) {
void addTrigger(Map<String, Object> m) {
this.addTrigger(new Trigger(this, m));
}
 
509,7 → 508,7
 
final String policy = getSchema().getFwkMetadata(UNDEFINED_ID_POLICY);
if (Boolean.getBoolean(debugUndef) || "min".equals(policy)) {
final SQLSelect sel = new SQLSelect(this.getBase(), true).addSelect(pk, "min");
final SQLSelect sel = new SQLSelect(true).addSelect(pk, "min");
final Number undef = (Number) this.getBase().getDataSource().executeScalar(sel.asString());
if (undef == null) {
// empty table
904,7 → 903,7
if (!this.isOrdered())
throw new IllegalStateException(this + " is not ordered");
 
final SQLSelect sel = new SQLSelect(this.getBase(), true).addSelect(this.getOrderField(), "max");
final SQLSelect sel = new SQLSelect(true).addSelect(this.getOrderField(), "max");
try {
final BigDecimal maxOrder = (BigDecimal) this.getBase().getDataSource().execute(sel.asString(), new IResultSetHandler(SQLDataSource.SCALAR_HANDLER, useCache));
return maxOrder == null ? BigDecimal.ONE.negate() : maxOrder;
982,7 → 981,7
final List<Tuple3<SQLRow, SQLField, SQLRow>> inconsistencies = new ArrayList<Tuple3<SQLRow, SQLField, SQLRow>>();
// si on a pas de relation externe, c'est OK
if (!fks.isEmpty()) {
SQLSelect sel = new SQLSelect(this.getBase());
final SQLSelect sel = new SQLSelect();
// on ne vérifie pas les lignes archivées mais l'indéfinie oui.
sel.setExcludeUndefined(false);
sel.addSelect(pk);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesCluster.java
16,6 → 16,7
import org.openconcerto.sql.model.SQLRowValues.ForeignCopyMode;
import org.openconcerto.sql.model.SQLRowValues.ReferentChangeEvent;
import org.openconcerto.sql.model.SQLRowValues.ReferentChangeListener;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.utils.CollectionMap;
31,6 → 32,7
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EventObject;
237,9 → 239,12
 
// and link them together
for (final SQLRowValues n : this.getItems()) {
for (final Entry<String, SQLRowValues> e : n.getForeigns().entrySet())
noLinkCopy.get(n).put(e.getKey(), noLinkCopy.get(e.getValue()));
// use referents instead of foreigns to copy order
for (final Entry<SQLField, Collection<SQLRowValues>> e : n.getReferents().entrySet())
for (final SQLRowValues ref : e.getValue()) {
noLinkCopy.get(ref).put(e.getKey().getName(), noLinkCopy.get(n));
}
}
 
return noLinkCopy.get(v);
}
393,16 → 398,16
* @return the exception that stopped the recursion, <code>null</code> if none was thrown.
*/
public final <T> StopRecurseException walk(final SQLRowValues start, T acc, ITransformer<State<T>, T> closure, RecursionType recType) {
return this.walk(start, acc, closure, recType, true);
return this.walk(start, acc, closure, recType, Direction.FOREIGN);
}
 
public final <T> StopRecurseException walk(final SQLRowValues start, T acc, ITransformer<State<T>, T> closure, RecursionType recType, final Boolean foreign) {
public final <T> StopRecurseException walk(final SQLRowValues start, T acc, ITransformer<State<T>, T> closure, RecursionType recType, final Direction foreign) {
return this.walk(start, acc, closure, recType, foreign, true);
}
 
final <T> StopRecurseException walk(final SQLRowValues start, T acc, ITransformer<State<T>, T> closure, RecursionType recType, final Boolean foreign, final boolean includeStart) {
final <T> StopRecurseException walk(final SQLRowValues start, T acc, ITransformer<State<T>, T> closure, RecursionType recType, final Direction foreign, final boolean includeStart) {
this.containsCheck(start);
return this.walk(new State<T>(Collections.singletonList(start), new Path(start.getTable()), acc, closure), recType, foreign, includeStart);
return this.walk(new State<T>(Collections.singletonList(start), Path.get(start.getTable()), acc, closure), recType, foreign, includeStart);
}
 
/**
411,13 → 416,12
* @param <T> type of acc.
* @param state the current position in the graph.
* @param recType how to recurse.
* @param foreign <code>true</code> to follow foreign keys, <code>false</code> to go the other
* way, <code>null</code> for both.
* @param direction how to cross foreign keys.
* @param computeThisState <code>false</code> if the <code>state</code> should not be
* {@link State#compute() computed}.
* @return the exception that stopped the recursion, <code>null</code> if none was thrown.
*/
private final <T> StopRecurseException walk(final State<T> state, RecursionType recType, final Boolean foreign, final boolean computeThisState) {
private final <T> StopRecurseException walk(final State<T> state, RecursionType recType, final Direction direction, final boolean computeThisState) {
if (computeThisState && recType == RecursionType.BREADTH_FIRST) {
final StopRecurseException e = state.compute();
if (e != null)
425,13 → 429,13
}
// get the foreign or referents rowValues
StopRecurseException res = null;
if (foreign == null || Boolean.TRUE.equals(foreign)) {
res = rec(state, recType, foreign, true);
if (direction != Direction.REFERENT) {
res = rec(state, recType, direction, Direction.FOREIGN);
}
if (res != null)
return res;
if (foreign == null || Boolean.FALSE.equals(foreign)) {
res = rec(state, recType, foreign, false);
if (direction != Direction.FOREIGN) {
res = rec(state, recType, direction, Direction.REFERENT);
}
if (res != null)
return res;
444,16 → 448,18
return null;
}
 
private <T> StopRecurseException rec(final State<T> state, RecursionType recType, final Boolean foreign, final boolean actualForeign) {
private <T> StopRecurseException rec(final State<T> state, RecursionType recType, final Direction direction, final Direction actualDirection) {
final SQLRowValues current = state.getCurrent();
final List<SQLRowValues> currentValsPath = state.getValsPath();
final CollectionMap<SQLField, SQLRowValues> nextVals;
if (actualForeign) {
if (actualDirection == Direction.FOREIGN) {
final Map<SQLField, SQLRowValues> foreigns = current.getForeignsBySQLField();
nextVals = new CollectionMap<SQLField, SQLRowValues>(new ArrayList<SQLRowValues>(), foreigns.size());
nextVals.putAll(foreigns);
} else
} else {
assert actualDirection == Direction.REFERENT;
nextVals = current.getReferents();
}
// predictable and repeatable order
final List<SQLField> keys = new ArrayList<SQLField>(nextVals.keySet());
Collections.sort(keys, FIELD_COMPARATOR);
461,11 → 467,10
for (final SQLRowValues v : nextVals.getNonNull(f)) {
// avoid infinite loop (don't use equals so that we can go over several equals rows)
if (!state.identityContains(v)) {
final Path path = new Path(state.getPath());
path.add(f, actualForeign);
final Path path = state.getPath().add(f, actualDirection);
final List<SQLRowValues> valsPath = new ArrayList<SQLRowValues>(currentValsPath);
valsPath.add(v);
final StopRecurseException e = this.walk(new State<T>(valsPath, path, state.getAcc(), state.closure), recType, foreign, true);
final StopRecurseException e = this.walk(new State<T>(valsPath, path, state.getAcc(), state.closure), recType, direction, true);
if (e != null && e.isCompletely())
return e;
}
475,7 → 480,7
}
 
final void walkFields(final SQLRowValues start, IClosure<FieldPath> closure, final boolean includeFK) {
walkFields(start, new Path(start.getTable()), Collections.singletonList(start), closure, includeFK);
walkFields(start, Path.get(start.getTable()), Collections.singletonList(start), closure, includeFK);
}
 
private void walkFields(final SQLRowValues current, final Path p, final List<SQLRowValues> currentValsPath, IClosure<FieldPath> closure, final boolean includeFK) {
488,8 → 493,7
final SQLRowValues newVals = foreigns.get(field);
// avoid infinite loop
if (!currentValsPath.contains(newVals)) {
final Path newP = new Path(p);
newP.add(current.getTable().getField(field), true);
final Path newP = p.add(current.getTable().getField(field), Direction.FOREIGN);
final List<SQLRowValues> newValsPath = new ArrayList<SQLRowValues>(currentValsPath);
newValsPath.add(newVals);
this.walkFields(newVals, newP, newValsPath, closure, includeFK);
649,7 → 653,7
});
}
}
}, RecursionType.DEPTH_FIRST, false);
}, RecursionType.DEPTH_FIRST, Direction.REFERENT);
 
return matrix.print(cellLength, new ITransformer<SQLRowValues, String>() {
@Override
684,15 → 688,17
return sb.toString();
}
 
final boolean equals(final SQLRowValues vals, final SQLRowValues other) {
final String getFirstDifference(final SQLRowValues vals, final SQLRowValues other) {
this.containsCheck(vals);
if (this == other.getGraph())
return true;
return null;
// don't call walk() if we can avoid as it is quite costly
if (this.size() != other.getGraph().size() || !vals.equalsJustThis(other))
return false;
if (this.size() != other.getGraph().size())
return "different size : " + this.size() + " != " + other.getGraph().size();
else if (!vals.equalsJustThis(other))
return "unequal :\n" + vals + " !=\n" + other;
if (this.size() == 1)
return true;
return null;
// BREADTH_FIRST no need to go deep if the first values are not equals
final List<SQLRowValues> flatList = new ArrayList<SQLRowValues>();
this.walk(vals, flatList, new ITransformer<State<List<SQLRowValues>>, List<SQLRowValues>>() {
701,7 → 707,7
input.getAcc().add(input.getCurrent());
return input.getAcc();
}
}, RecursionType.BREADTH_FIRST, null);
}, RecursionType.BREADTH_FIRST, Direction.ANY);
assert flatList.size() == this.size() : "missing rows";
// now walk the other graph, checking that each row is equal
// (this works because walk() always goes with the same order, see #FIELD_COMPARATOR)
709,12 → 715,14
final StopRecurseException stop = other.getGraph().walk(other, null, new ITransformer<State<Object>, Object>() {
@Override
public Object transformChecked(State<Object> input) {
if (!input.getCurrent().equalsJustThis(flatList.get(index.getAndIncrement())))
throw new StopRecurseException("unequal at " + input.getPath());
final SQLRowValues thisVals = flatList.get(index.getAndIncrement());
final SQLRowValues oVals = input.getCurrent();
if (!thisVals.equalsJustThis(oVals))
throw new StopRecurseException("unequal at " + input.getPath() + " :\n" + thisVals + " !=\n" + oVals);
return input.getAcc();
}
}, RecursionType.BREADTH_FIRST, null);
return stop == null;
}, RecursionType.BREADTH_FIRST, Direction.ANY);
return stop == null ? null : stop.getMessage();
}
 
static public final class StopRecurseException extends RuntimeException {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRequestLog.java
26,6 → 26,7
import java.awt.event.MouseEvent;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
67,7 → 68,7
private String threadId;
private static List<ChangeListener> listeners = new ArrayList<ChangeListener>(2);
private static JLabel textInfo = new JLabel("Total: ");
private static final SimpleDateFormat sdt = new SimpleDateFormat("HH:MM:ss.SS");
private static final DateFormat sdt = new SimpleDateFormat("HH:mm:ss.SS");
private static final DecimalFormat dformat = new DecimalFormat("##0.00");
 
private boolean isHighlighted = false;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowMode.java
89,7 → 89,7
return this.check(r) ? r : null;
}
 
public void filter(Collection rows) {
public void filter(Collection<SQLRow> rows) {
CollectionUtils.filter(rows, new Predicate() {
public boolean evaluate(Object object) {
return check((SQLRow) object);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxPG.java
85,7 → 85,7
public String getInitRoot(final String name) {
final String sql;
try {
final String fileContent = FileUtils.read(this.getClass().getResourceAsStream("pgsql-functions.sql"), "UTF8");
final String fileContent = FileUtils.read(SQLSyntaxPG.class.getResourceAsStream("pgsql-functions.sql"), "UTF8");
sql = fileContent.replace("${rootName}", SQLBase.quoteIdentifier(name));
} catch (IOException e) {
throw new IllegalStateException("cannot read functions", e);
212,7 → 212,7
protected String getCreateIndex(final String cols, final SQLName tableName, Index i) {
final String method = i.getMethod() != null ? " USING " + i.getMethod() : "";
// TODO handle where
return i.getTable().getBase().quote("ON %i " + method + cols, tableName);
return "ON " + tableName.quote() + " " + method + cols;
}
 
@SuppressWarnings("unused")
265,7 → 265,7
}
});
 
final String seq = FixSerial.getPrimaryKeySeq(t);
final SQLName seq = FixSerial.getPrimaryKeySeq(t);
// no need to alter sequence if nothing was inserted (can be -1 in old pg)
// also avoid NULL for empty tables and thus arbitrary start constant
if (count.intValue() != 0 && seq != null) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLImmutableRowValues.java
88,6 → 88,11
return this.delegate.isForeignEmpty(fieldName);
}
 
@Override
public int getForeignID(String fieldName) {
return this.delegate.getForeignID(fieldName);
}
 
public boolean isDefault(String fieldName) {
return this.delegate.isDefault(fieldName);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowAccessor.java
65,7 → 65,7
}
 
/**
* Creates an SQLRow from these values, without any db access.
* Creates an SQLRow from these values, without any DB access.
*
* @return an SQLRow with the same values as this.
*/
72,7 → 72,7
public abstract SQLRow asRow();
 
/**
* Creates an SQLRowValues from these values, without any db access.
* Creates an SQLRowValues from these values, without any DB access.
*
* @return an SQLRowValues with the same values as this.
*/
185,6 → 185,16
*/
public abstract SQLRowAccessor getForeign(String fieldName);
 
/**
* Return the ID of a foreign row.
*
* @param fieldName name of the foreign field.
* @return the value of <code>fieldName</code>, {@link SQLRow#NONEXISTANT_ID} if
* <code>null</code>.
* @throws IllegalArgumentException if fieldName is not a foreign field.
*/
public abstract int getForeignID(String fieldName) throws IllegalArgumentException;
 
public abstract boolean isForeignEmpty(String fieldName);
 
public abstract Collection<? extends SQLRowAccessor> getReferentRows();
/trunk/OpenConcerto/src/org/openconcerto/sql/preferences/SQLPreferences.java
78,8 → 78,7
private static final String PREF_NODE_TABLENAME = "PREF_NODE";
private static final String PREF_VALUE_TABLENAME = "PREF_VALUE";
 
static public SQLTable getPrefTable(final DBRoot root) throws SQLException {
if (!root.contains(PREF_VALUE_TABLENAME)) {
static private SQLCreateTable[] getCreateTables(final DBRoot root) throws SQLException {
final SQLCreateTable createNodeT = new SQLCreateTable(root, PREF_NODE_TABLENAME);
// don't need ORDER and ARCHIVE
createNodeT.setPlain(true);
100,10 → 99,17
createValueT.setPrimaryKey("ID_NODE", "NAME");
createValueT.addForeignConstraint("ID_NODE", new SQLName(createNodeT.getName()), SQLSyntax.ID_NAME);
 
root.createTables(createNodeT, createValueT);
return new SQLCreateTable[] { createNodeT, createValueT };
}
 
static public SQLTable getPrefTable(final DBRoot root) throws SQLException {
synchronized (root.getDBSystemRoot().getTreeMutex()) {
if (!root.contains(PREF_VALUE_TABLENAME)) {
root.createTables(getCreateTables(root));
}
return root.getTable(PREF_VALUE_TABLENAME);
}
}
 
private static final Map<DBRoot, SQLPreferences> memCachedbyRoots = new IdentityHashMap<DBRoot, SQLPreferences>(8);
 
/trunk/OpenConcerto/src/org/openconcerto/sql/users/AccesSocieteTable.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/users/UserCommonSQLElement.java
18,17 → 18,29
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.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.sqlobject.itemview.SimpleRowItemView;
import org.openconcerto.sql.ui.Login;
import org.openconcerto.sql.view.QuickAssignPanel;
import org.openconcerto.sql.view.list.RowValuesTableModel;
import org.openconcerto.sql.view.list.SQLTableElement;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.ISpinner;
import org.openconcerto.ui.ISpinnerIntegerModel;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.valuewrapper.TextValueWrapper;
import org.openconcerto.ui.warning.JLabelWarning;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.checks.ValidState;
import org.openconcerto.utils.i18n.I18nUtils;
import org.openconcerto.utils.text.SimpleDocumentListener;
36,6 → 48,11
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
 
101,10 → 118,17
 
private JPasswordField passField, passFieldConfirm;
private JPanel panelWarning;
private AccesSocieteTable table;
private final SQLElement accessSocieteElem;
private QuickAssignPanel table;
// TODO transform into real SQLRowItemView
private final JTextField encryptedPass = new JTextField();
private JCheckBox noCompanyLimitCheckBox;
 
{
final SQLTable t = getTable().getDBSystemRoot().getGraph().findReferentTable(getTable(), "ACCES_SOCIETE");
this.accessSocieteElem = t == null ? null : getDirectory().getElement(t);
}
 
protected final JPasswordField getPassField() {
return this.passField;
}
125,7 → 149,7
c.anchor = GridBagConstraints.WEST;
 
this.panelWarning = new JPanel(new GridBagLayout());
this.panelWarning.setBorder(null);
this.panelWarning.setBorder(BorderFactory.createEmptyBorder());
final JLabelWarning labelWarning = new JLabelWarning();
// labelWarning.setBorder(null);
this.panelWarning.add(labelWarning, c);
220,10 → 244,10
this.add(textSurnom, c);
 
if (this.getTable().contains("ADMIN")) {
final JCheckBox checkAdmin = new JCheckBox(getLabelFor("ADMIN"));
c.gridx++;
c.gridwidth = GridBagConstraints.REMAINDER;
c.weightx = 0;
final JCheckBox checkAdmin = new JCheckBox(getLabelFor("ADMIN"));
this.add(checkAdmin, c);
this.addView(checkAdmin, "ADMIN");
}
242,7 → 266,6
final JTextField textMail = new JTextField();
c.gridwidth = GridBagConstraints.REMAINDER;
c.weightx = 1;
c.weighty = 1;
 
this.add(textMail, c);
this.addView(textMail, "MAIL");
265,12 → 288,45
c.weighty = 0;
panelHoraires.setBorder(BorderFactory.createTitledBorder("Horaires"));
this.add(panelHoraires, c);
}
 
if (this.accessSocieteElem != null) {
c.gridy++;
c.gridx = 0;
c.gridwidth = 4;
 
this.add(new JLabelBold("Accés aux sociétés"), c);
c.gridy++;
noCompanyLimitCheckBox = new JCheckBox("ne pas limiter l'accès à certaines sociétés");
this.add(noCompanyLimitCheckBox, c);
 
c.gridy++;
c.weighty = 1;
this.table = new AccesSocieteTable();
c.fill = GridBagConstraints.BOTH;
final SQLElement companyElement = this.accessSocieteElem.getForeignElement("ID_SOCIETE_COMMON");
final List<SQLTableElement> tableElements = new ArrayList<SQLTableElement>();
tableElements.add(new SQLTableElement(companyElement.getTable().getField("NOM")));
final RowValuesTableModel model = new RowValuesTableModel(companyElement, tableElements, companyElement.getTable().getKey(), false);
this.table = new QuickAssignPanel(companyElement, "ID", model);
this.add(this.table, c);
 
noCompanyLimitCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
table.setEnabled(e.getStateChange() == ItemEvent.DESELECTED);
}
});
getView("ADMIN").addValueListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
final boolean selected = evt.getNewValue() == Boolean.TRUE;
noCompanyLimitCheckBox.setEnabled(!selected);
if (selected)
noCompanyLimitCheckBox.setSelected(true);
}
});
noCompanyLimitCheckBox.setSelected(true);
}
 
this.addRequiredSQLObject(textLogin, "LOGIN");
this.addView(new SimpleRowItemView<String>(new TextValueWrapper(this.encryptedPass)) {
358,14 → 414,35
// so that this.encryptedPass (which will be updated by updateEncrypted() and thus
// have a bogus value) will be changed to its database value and thus the user can
// update any field without changing the password.
if (table != null) {
table.getModel().clearRows();
}
if (row != null) {
final String bogusPass = "bogusPass!";
this.getPassField().setText(bogusPass);
this.getPassFieldConfirm().setText(bogusPass);
if (this.table != null) {
this.table.insertFrom("ID_USER_COMMON", row.getID());
// Allowed companies
if (this.accessSocieteElem != null) {
final SQLTable tableCompanyAccess = this.accessSocieteElem.getTable();
SQLRowValues graph = new SQLRowValues(tableCompanyAccess);
graph.put("ID", null);
graph.putRowValues("ID_SOCIETE_COMMON").put("NOM", null);
SQLRowValuesListFetcher f = new SQLRowValuesListFetcher(graph);
f.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(SQLSelect input) {
input.setWhere(new Where(tableCompanyAccess.getField("ID_USER_COMMON"), "=", row.getID()));
return input;
}
});
List<SQLRowValues> companies = f.fetch();
 
for (SQLRowValues r : companies) {
table.getModel().addRow(r.getForeign("ID_SOCIETE_COMMON").asRowValues());
}
noCompanyLimitCheckBox.setSelected(companies.isEmpty());
}
}
super.select(row);
}
 
372,19 → 449,40
@Override
public int insert(SQLRow order) {
int id = super.insert(order);
if (this.table != null) {
this.table.updateField("ID_USER_COMMON", id);
if (this.table != null && !noCompanyLimitCheckBox.isSelected()) {
insertCompanyAccessForUser(id);
}
return id;
}
 
private void insertCompanyAccessForUser(int id) {
final SQLTable tableCompanyAccess = this.getDirectory().getElement("ACCES_SOCIETE").getTable();
int stop = table.getModel().getRowCount();
for (int i = 0; i < stop; i++) {
SQLRowValues rCompany = table.getModel().getRowValuesAt(i);
SQLRowValues rAccess = new SQLRowValues(tableCompanyAccess);
rAccess.put("ID_USER_COMMON", id);
rAccess.put("ID_SOCIETE_COMMON", rCompany.getID());
try {
rAccess.commit();
} catch (SQLException e) {
ExceptionHandler.handle("Unable to store company access", e);
}
}
}
 
@Override
public void update() {
super.update();
if (this.table != null) {
this.table.updateField("ID_USER_COMMON", getSelectedID());
final SQLTable tableCompanyAccess = this.getDirectory().getElement("ACCES_SOCIETE").getTable();
String query = "DELETE FROM " + tableCompanyAccess.getSQL() + " WHERE \"ID_USER_COMMON\" = " + getSelectedID();
tableCompanyAccess.getDBSystemRoot().getDataSource().execute(query);
if (!noCompanyLimitCheckBox.isSelected()) {
insertCompanyAccessForUser(getSelectedID());
}
}
}
};
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/CompanyAccessSQLElement.java
New file
0,0 → 1,44
/*
* 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.users;
 
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;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightsManager.java
29,6 → 29,7
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.CompareUtils.Equalizer;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple3;
import org.openconcerto.utils.cc.IFactory;
48,6 → 49,7
 
public static final String USER_RIGHT_TABLE = UserRightSQLElement.TABLE_NAME;
public static final String SUPERUSER_FIELD = "SUPERUSER";
private static final int ADMIN_ID = SQLRow.NONEXISTANT_ID;
/**
* Only administrators can see user rights.
*/
57,6 → 59,8
private static final CollectionMap<String, Tuple2<String, Boolean>> NO_RIGHTS = CollectionMap.singleton(null, Tuple2.create((String) null, false));
public static final List<MacroRight> DEFAULT_MACRO_RIGHTS = Collections.synchronizedList(new ArrayList<MacroRight>());
static {
// "addRight() ambiguous"
assert ADMIN_ID < SQLRow.MIN_VALID_ID;
DEFAULT_MACRO_RIGHTS.add(new LockAdminUserRight());
DEFAULT_MACRO_RIGHTS.add(new TableAllRights(true));
DEFAULT_MACRO_RIGHTS.add(new TableAllRights(false));
145,7 → 149,8
private final SQLTable table;
@GuardedBy("this")
private SQLTableModifiedListener tableL;
private final CollectionMap<Integer, RightTuple> javaRights;
@GuardedBy("rights")
private final ListMap<Integer, RightTuple> javaRights;
 
private UserRightsManager(final SQLTable t) {
if (t == null)
152,7 → 157,7
throw new NullPointerException("Missing table");
this.macroRights = new HashMap<String, MacroRight>();
this.rights = new HashMap<Integer, CollectionMap<String, Tuple2<String, Boolean>>>();
this.javaRights = new CollectionMap<Integer, RightTuple>();
this.javaRights = new ListMap<Integer, RightTuple>();
this.table = t;
this.tableL = new SQLTableModifiedListener() {
@Override
185,16 → 190,63
}
 
/**
* Add an inconditional right for the passed user. Ie it is loaded before the sql ones.
* Add an unconditional right for the passed user. I.e. it is loaded before the SQL ones and
* even default unconditional rights are before user SQL rights.
*
* @param userID the user id, <code>null</code> meaning for everyone.
* @param right the right the user should always have.
*/
public void addRight(Integer userID, RightTuple right) {
this.javaRights.put(userID, right);
if (right == null)
throw new NullPointerException("Null right entry");
synchronized (this.rights) {
this.javaRights.add(getKey(userID), right);
this.rightsInvalid();
}
}
 
/**
* Add an unconditional right for administrators. This will be after user rights and before
* default rights.
*
* @param right the right to add.
*/
public void addRightForAdmins(RightTuple right) {
if (right == null)
throw new NullPointerException("Null right entry");
synchronized (this.rights) {
this.javaRights.add(ADMIN_ID, right);
this.rightsInvalid();
}
}
 
private final int getKey(final Integer userID) {
if (userID != null && userID < SQLRow.MIN_VALID_ID)
throw new IllegalArgumentException("invalid ID : " + userID);
return userID == null ? getDefaultUserId() : userID;
}
 
/**
* Remove a right.
*
* @param userID the user id, <code>null</code> meaning default user.
* @param right the right to remove, <code>null</code> meaning remove all.
* @see #addRight(Integer, RightTuple)
*/
public void removeRight(final Integer userID, final RightTuple right) {
synchronized (this.rights) {
if (right == null)
this.javaRights.remove(getKey(userID));
else
this.javaRights.remove(getKey(userID), right);
this.rightsInvalid();
}
}
 
public void removeRightForAdmins(final RightTuple right) {
this.removeRight(ADMIN_ID, right);
}
 
public synchronized final boolean isValid() {
return this.tableL != null;
}
232,9 → 284,9
* the right is <code>null</code> or <code>objectMatcher</code> returns <code>true</code> when
* passed both objects. There's also a special case if <code>object</code> is <code>null</code>
* : in that case all found objects must be allowed until a right with a <code>null</code>
* object. With these rules setting the object of the right to <code>null</code> means giving
* the right to any object. And searching for the object <code>null</code> means asking if the
* right is allowed for all the objects. <br>
* object for the right to be granted. With these rules setting the object of the right to
* <code>null</code> means giving the right to any object. And searching for the object
* <code>null</code> means asking if the right is allowed for all the objects. <br>
* For example if you have these rights (* meaning <code>null</code>) :
* <ol>
* <li>del T yes</li>
243,7 → 295,7
* <li>ins * yes</li>
* <li>del * yes</li>
* </ol>
* then you can delete from T but not insert ; you can however to both on any other object. If
* then you can delete from T but not insert ; you can however do both on any other object. If
* you pass <code>null</code> for <code>object</code>, it will return <code>true</code> for del,
* but <code>false</code> for ins.
*
259,9 → 311,12
final Boolean userRight = haveRightP(userID, code, requestedObject, objectMatcher, unicity);
if (userRight != null)
return userRight;
final Boolean defaultRight = haveRightP(getDefaultUserId(), code, requestedObject, objectMatcher, unicity);
final int defaultUser = getDefaultUserId();
if (defaultUser != userID) {
final Boolean defaultRight = haveRightP(defaultUser, code, requestedObject, objectMatcher, unicity);
if (defaultRight != null)
return defaultRight;
}
 
return false;
}
347,15 → 402,25
// only superuser can modify RIGHTs
expand(res, unicity, TableAllRights.createRight(TableAllRights.CODE_MODIF, this.getTable().getForeignTable("ID_RIGHT"), false));
// only admin can modify or see USER_RIGHTs
expand(res, unicity, TableAllRights.createRight(TableAllRights.CODE, this.getTable(), userRow != null && userRow.getBoolean(ADMIN_FIELD)));
final boolean isAdmin = userRow != null && userRow.getBoolean(ADMIN_FIELD);
expand(res, unicity, TableAllRights.createRight(TableAllRights.CODE, this.getTable(), isAdmin));
 
// java rights have priority over SQL rights
for (final RightTuple t : this.javaRights.getNonNull(userID)) {
expand(res, unicity, t);
}
for (final RightTuple t : this.javaRights.getNonNull(null)) {
// perhaps allow SQL to also specify admin rights
if (isAdmin) {
for (final RightTuple t : this.javaRights.getNonNull(ADMIN_ID))
expand(res, unicity, t);
}
final int defaultUser = getDefaultUserId();
if (defaultUser != userID) {
// even default java rights are before user SQL rights
for (final RightTuple t : this.javaRights.getNonNull(defaultUser)) {
expand(res, unicity, t);
}
}
 
final SQLRowValues vals = new SQLRowValues(getTable()).setAllToNull();
vals.putRowValues("ID_RIGHT").setAllToNull();
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightsPanel.java
55,6 → 55,8
// instantiate our own element to be safe
this.modifPanel = new ListeModifyPanel(dir.getElement(UserRightSQLElement.class));
this.modifPanel.setSearchFullMode(false);
// order is important for rights
this.modifPanel.getListe().setSortingEnabled(false);
final SQLTable table = this.getTable().getForeignTable("ID_USER_COMMON");
 
final SQLElement usersElem = dir.getElement(table);
/trunk/OpenConcerto/src/org/openconcerto/utils/DesktopEnvironment.java
13,6 → 13,8
package org.openconcerto.utils;
 
import org.openconcerto.utils.OSFamily.Unix;
 
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
230,12 → 232,12
}
 
private static final DesktopEnvironment detectDE() {
final String os = System.getProperty("os.name");
if (os.startsWith("Windows")) {
final OSFamily os = OSFamily.getInstance();
if (os == OSFamily.Windows) {
return new Windows();
} else if (os.startsWith("Mac OS")) {
} else if (os == OSFamily.Mac) {
return new Mac();
} else if (os.startsWith("Linux")) {
} else if (os instanceof Unix) {
// from redhat xdg-utils 1.0.2-14.20091016cvs
if ("true".equalsIgnoreCase(System.getenv("KDE_FULL_SESSION")))
return new KDE();
/trunk/OpenConcerto/src/org/openconcerto/utils/text/DocumentFilterList.java
51,10 → 51,13
* have a DocumentFilterList as its filter.
*
* @param doc the document.
* @param df the filter to add
* @param df the filter to add, <code>null</code> meaning none.
* @param t the type of filter <code>df</code> is, used to determine where to add.
*/
public static void add(AbstractDocument doc, DocumentFilter df, FilterType t) {
// behave like Component.addListener()
if (df == null)
return;
if (t == FilterType.SIMPLE_FILTER)
get(doc).getFilters().add(0, df);
else
62,6 → 65,28
}
 
/**
* Remove a filter from the filters for the passed doc. The document doesn't need to have a
* DocumentFilterList as its filter.
*
* @param doc the document.
* @param df the filter to remove, <code>null</code> meaning none.
*/
public static void remove(AbstractDocument doc, DocumentFilter df) {
// behave like Component.removeListener()
if (df == null)
return;
final DocumentFilter filter = doc.getDocumentFilter();
if (filter == null)
return;
 
if (filter.equals(df)) {
doc.setDocumentFilter(null);
} else if (filter instanceof DocumentFilterList) {
((DocumentFilterList) filter).getFilters().remove(df);
}
}
 
/**
* Return the DocumentFilterList of the passed doc, creating one if needed.
*
* @param doc the document.
/trunk/OpenConcerto/src/org/openconcerto/utils/CompareUtils.java
82,6 → 82,19
}
}
 
static private final Comparator<Comparable<Object>> NATURAL_COMPARATOR = new Comparator<Comparable<Object>>() {
@Override
public int compare(Comparable<Object> o1, Comparable<Object> o2) {
return o1.compareTo(o2);
}
};
 
// added in Comparator in Java 8
@SuppressWarnings("unchecked")
static public final <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) NATURAL_COMPARATOR;
}
 
/**
* Renvoie un comparateur qui utilise successivement la liste passée tant que les objets sont
* égaux.
/trunk/OpenConcerto/src/org/openconcerto/utils/PropertiesUtils.java
59,7 → 59,7
}
}
 
public static final void load(final Properties props, final Properties toLoad) throws IOException {
public static final void load(final Properties props, final Properties toLoad) {
for (final String key : toLoad.stringPropertyNames()) {
final String value = toLoad.getProperty(key);
assert value != null;
/trunk/OpenConcerto/src/org/openconcerto/utils/prog/RemoteDebugArgs.java
28,16 → 28,16
* @param args not used.
*/
public static void main(String[] args) {
final Date now = new Date();
final String debugArgs;
if (Boolean.getBoolean("remoteDebug")) {
final String time = DBG_TIME_FORMAT.format(now);
final String debugPort = time.substring(0, time.length() - 1);
debugArgs = " -agentlib:jdwp=transport=dt_socket,address=" + debugPort + ",server=y,suspend=n";
} else
debugArgs = "";
final String debugArgs = Boolean.getBoolean("remoteDebug") ? getArgs() : "";
 
// do not output newline to avoid problem on cygwin (\r)
System.out.print(debugArgs);
}
 
public static String getArgs() {
final Date now = new Date();
final String time = DBG_TIME_FORMAT.format(now);
final String debugPort = time.substring(0, time.length() - 1);
return "-agentlib:jdwp=transport=dt_socket,address=" + debugPort + ",server=y,suspend=n";
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/prog/VMLauncher.java
New file
0,0 → 1,406
/*
* 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.prog;
 
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.OSFamily;
import org.openconcerto.utils.ProcessStreams;
import org.openconcerto.utils.ProcessStreams.Action;
import org.openconcerto.utils.PropertiesUtils;
 
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
/**
* A class meant to be used as the main class of a jar and which launch another instance of the Java
* VM.
*
* @author Sylvain
* @see #launch(String, List)
*/
public abstract class VMLauncher {
 
/**
* Boolean system property, if set to <code>true</code> then {@link #restart(Class, List)} will
* simply return <code>null</code>. Useful e.g. when using IDE launch configuration (to debug).
*/
static public final String NO_RESTART = "vm.noRestart";
 
private static NativeLauncherFinder getNativeAppLauncher() {
final OSFamily os = OSFamily.getInstance();
final NativeLauncherFinder l;
if (os.equals(OSFamily.Windows)) {
l = new WinLauncherFinder();
} else if (os.equals(OSFamily.Mac)) {
l = new MacLauncherFinder();
} else {
l = UnknownLauncherFinder;
}
return l;
}
 
private static List<String> getNativeCommand(List<String> args) {
final NativeLauncherFinder l = getNativeAppLauncher();
return l.getAppPath() == null ? null : l.getCommand(args);
}
 
/**
* Allow to find out if the running VM was launched using a native application.
*
* @author Sylvain
*/
private static abstract class NativeLauncherFinder {
private final String cp, firstItem;
 
public NativeLauncherFinder() {
this.cp = ManagementFactory.getRuntimeMXBean().getClassPath();
final int sepIndex = this.cp.indexOf(File.pathSeparatorChar);
this.firstItem = sepIndex < 0 ? this.cp : this.cp.substring(0, sepIndex);
}
 
public final String getClassPath() {
return this.cp;
}
 
public final String getFirstItem() {
return this.firstItem;
}
 
/**
* The path to the native application if any.
*
* @return the path, <code>null</code> if no native application could be found.
*/
public abstract String getAppPath();
 
/**
* The command to launch this application with the passed arguments.
*
* @param args the program arguments.
* @return the command.
*/
public abstract List<String> getCommand(final List<String> args);
}
 
private static class MacLauncherFinder extends NativeLauncherFinder {
private static final String APP_EXT = ".app";
private static final Pattern MAC_PATTERN = Pattern.compile(Pattern.quote(APP_EXT) + "/Contents/Resources(/Java)?/[^/]+\\.jar$");
 
@Override
public String getAppPath() {
final Matcher matcher = MAC_PATTERN.matcher(this.getFirstItem());
if (matcher.matches()) {
final String appPath = getFirstItem().substring(0, matcher.start() + APP_EXT.length());
final File contentsDir = new File(appPath, "Contents");
final List<String> bundleContent = Arrays.asList(contentsDir.list());
if (bundleContent.contains("Info.plist") && bundleContent.contains("PkgInfo") && new File(contentsDir, "MacOS").isDirectory())
return appPath;
}
return null;
}
 
@Override
public List<String> getCommand(List<String> args) {
final List<String> command = new ArrayList<String>(4 + args.size());
command.add("open");
// since we restarting we need to launch a new instance of us
command.add("-n");
command.add(getAppPath());
command.add("--args");
command.addAll(args);
return command;
}
}
 
private static class WinLauncherFinder extends NativeLauncherFinder {
@Override
public String getAppPath() {
// launch4j
if (this.getFirstItem().endsWith(".exe"))
return getFirstItem();
else
return null;
}
 
@Override
public List<String> getCommand(List<String> args) {
final List<String> command = new ArrayList<String>(4 + args.size());
command.add(getAppPath());
command.addAll(args);
return command;
}
}
 
private static final NativeLauncherFinder UnknownLauncherFinder = new NativeLauncherFinder() {
@Override
public String getAppPath() {
return null;
}
 
@Override
public List<String> getCommand(List<String> args) {
throw new UnsupportedOperationException();
}
};
 
public static final Process restart(final Class<?> mainClass, final String... args) throws IOException {
return restart(mainClass, Arrays.asList(args));
}
 
public static final Process restart(final Action action, final Class<?> mainClass, final String... args) throws IOException {
return restart(action, mainClass, Arrays.asList(args));
}
 
/**
* Restart the VM. If this VM was launched using a native application (e.g. .exe or .app) then
* this will be executed. Else the <code>mainClass</code> will be used.
*
* @param mainClass the main() to use (if no native application was found).
* @param args the program arguments to pass.
* @return the new process, <code>null</code> if the program wasn't started.
* @throws IOException if the VM couldn't be launched.
* @see #NO_RESTART
*/
public static final Process restart(final Class<?> mainClass, final List<String> args) throws IOException {
return restart(Action.CLOSE, mainClass, args);
}
 
public static final Process restart(final Action action, final Class<?> mainClass, final List<String> args) throws IOException {
if (Boolean.getBoolean(NO_RESTART))
return null;
final File wd = FileUtils.getWD();
final List<String> command = getNativeCommand(args);
if (command != null) {
return ProcessStreams.handle(new ProcessBuilder(command).directory(wd).start(), action);
} else {
try {
mainClass.getMethod("main", String[].class);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(mainClass + " doesn't containt a main()", e);
}
return new VMLauncher() {
@Override
protected File getWD() {
return wd;
}
 
@Override
protected File getPropFile(String mainClass) {
return null;
}
 
@Override
protected Action getStreamAction() {
return action;
}
}.launch(mainClass.getName(), args);
}
}
 
public static final String ENV_VMARGS = "JAVA_VMARGS";
public static final String PROPS_VMARGS = "VMARGS";
public static final String ENV_PROGARGS = "JAVA_PROGARGS";
 
// handle DOS, Mac and Unix newlines
private static final Pattern NL = Pattern.compile("\\p{Cntrl}+");
 
private File wd;
 
public VMLauncher() {
this.wd = null;
}
 
public final File getLauncherWD() {
if (this.wd == null) {
final NativeLauncherFinder nativeAppLauncher = getNativeAppLauncher();
final String appPath = nativeAppLauncher.getAppPath();
if (appPath != null)
this.wd = new File(appPath).getAbsoluteFile().getParentFile();
// when launched with -jar there's only one item
else if (nativeAppLauncher.getFirstItem().equals(nativeAppLauncher.getClassPath()) && new File(nativeAppLauncher.getFirstItem()).isFile())
this.wd = new File(nativeAppLauncher.getFirstItem()).getParentFile();
// support launch in an IDE
else
this.wd = FileUtils.getWD();
}
return this.wd;
}
 
private final List<String> split(String res) {
res = res.trim();
if (res.length() == 0) {
return Collections.emptyList();
} else {
return Arrays.asList(NL.split(res));
}
}
 
private final List<String> getProp(final File propFile, final String propName) {
return this.getProp(this.getProps(propFile), propName);
}
 
private final Properties getProps(final File propFile) {
if (propFile != null && propFile.canRead()) {
try {
return PropertiesUtils.createFromFile(propFile);
} catch (IOException e) {
e.printStackTrace();
}
}
return new Properties();
}
 
private final List<String> getProp(final Properties props, final String propName) {
String res = "";
if (props != null) {
res = props.getProperty(propName, res);
}
return split(res);
}
 
protected final Process launch(final String mainClass) throws IOException {
return this.launch(mainClass, Collections.<String> emptyList());
}
 
/**
* Launch a new Java VM. This method will try to launch {@link #getJavaBinary() java} from the
* same installation, if that fails it will use the system path for binaries. VM arguments can
* be specified with :
* <ol>
* <li>the {@value #ENV_VMARGS} environment variable</li>
* <li>the {@value #PROPS_VMARGS} property in {@link #getPropFile(String)}</li>
* <li>the {@link #getVMArguments()} method</li>
* </ol>
* Program arguments :
* <ol>
* <li>the <code>progParams</code> parameter</li>
* <li>the {@value #ENV_PROGARGS} environment variable</li>
* </ol>
*
* @param mainClass the main class.
* @param progParams the program arguments for <code>mainClass</code>.
* @return the new Process.
* @throws IOException if the process couldn't be started.
*/
protected final Process launch(final String mainClass, final List<String> progParams) throws IOException {
final boolean debug = Boolean.getBoolean("launcher.debug");
final String javaBinary = getJavaBinary();
final File sameJava = new File(System.getProperty("java.home"), "bin/" + javaBinary);
final String java = sameJava.canExecute() ? sameJava.getAbsolutePath() : javaBinary;
final File propFile = this.getPropFile(mainClass);
final Properties props = this.getProps(propFile);
if (debug)
System.err.println("propFile : " + propFile);
 
final List<String> command = new ArrayList<String>();
command.add(java);
 
if (this.enableRemoteDebug(props)) {
command.add(RemoteDebugArgs.getArgs());
}
command.addAll(this.getVMArguments());
 
// for java the last specified property wins
if (propFile != null) {
final List<String> appProps = this.getProp(props, PROPS_VMARGS);
command.addAll(appProps);
final File userFile = new File(System.getProperty("user.home"), ".java/ilm/" + propFile.getName());
final List<String> userProps = this.getProp(userFile, PROPS_VMARGS);
command.addAll(userProps);
if (debug) {
System.err.println("appProps : " + appProps);
System.err.println("userProps ( from " + userFile + ") : " + userProps);
}
}
final String envVMArgs = System.getenv(ENV_VMARGS);
if (envVMArgs != null)
command.addAll(split(envVMArgs));
 
command.add("-cp");
command.add(getClassPath());
command.add(mainClass);
final String envProgArgs = System.getenv(ENV_PROGARGS);
if (envProgArgs != null)
command.addAll(split(envProgArgs));
command.addAll(progParams);
 
// inherit environment so that the next launch() can access the same variables
final ProcessBuilder procBuilder = new ProcessBuilder(command).directory(getWD());
this.modifyEnv(procBuilder.environment());
if (debug) {
System.err.println("Command line : " + procBuilder.command());
System.err.println("Dir : " + procBuilder.directory());
System.err.println("Std out and err :");
}
 
final Process res = procBuilder.start();
ProcessStreams.handle(res, debug ? Action.REDIRECT : this.getStreamAction());
 
return res;
}
 
protected void modifyEnv(Map<String, String> environment) {
}
 
protected Action getStreamAction() {
return Action.CLOSE;
}
 
protected boolean enableRemoteDebug(Properties props) {
final String prop = props.getProperty("remoteDebug");
return prop == null ? remoteDebugDefault() : Boolean.parseBoolean(prop);
}
 
protected boolean remoteDebugDefault() {
return false;
}
 
/**
* The program to launch. This implementation returns <code>javaw</code> for Windows and
* <code>java</code> for other OS.
*
* @return the name of the binary.
*/
protected String getJavaBinary() {
return OSFamily.getInstance() == OSFamily.Windows ? "javaw" : "java";
}
 
protected List<String> getVMArguments() {
return Arrays.asList("-Dfile.encoding=UTF-8", "-Xms100M", "-Xmx256M");
}
 
// by default in the same jar
protected String getClassPath() {
return ManagementFactory.getRuntimeMXBean().getClassPath();
}
 
// by default in the same directory
protected File getWD() {
return this.getLauncherWD();
}
 
protected File getPropFile(final String mainClass) {
final String className = mainClass.substring(mainClass.lastIndexOf('.') + 1);
return new File(getWD(), className + ".properties");
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/XMLDateFormat.java
14,43 → 14,55
package org.openconcerto.utils;
 
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
 
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
 
/**
* Format a {@link Date} according to <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">W3C XML
* Schema 1.0 Part 2, Section 3.2.7-14</a>.
* Format an absolute {@link Date} according to <a
* href="http://www.w3.org/TR/xmlschema-2/#dateTime">W3C XML Schema 1.0 Part 2, Section
* 3.2.7-14</a>. I.e. {@link #format(Object)} always write a time zone part and
* {@link #parseObject(String)} will only use the {@link #getTimeZone() default time zone} if none
* is specified.
*
* @author Sylvain CUAZ
* @see XMLGregorianCalendar
* @see XMLCalendarFormat
*/
public class XMLDateFormat extends Format {
public class XMLDateFormat extends AbstractXMLDateFormat {
 
private final static DatatypeFactory factory;
static {
try {
factory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
// shouldn't happen since an implementation is provided with the jre
throw new IllegalStateException(e);
public XMLDateFormat() {
this(null, null);
}
 
public XMLDateFormat(final TimeZone timezone, final Locale aLocale) {
super(timezone, aLocale);
}
 
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
final GregorianCalendar cal;
if (obj instanceof GregorianCalendar)
if (obj instanceof GregorianCalendar) {
cal = (GregorianCalendar) obj;
else {
cal = new GregorianCalendar();
cal.setTime((Date) obj);
} else {
final Date d;
final TimeZone tz;
if (obj instanceof Calendar) {
d = ((Calendar) obj).getTime();
tz = ((Calendar) obj).getTimeZone();
} else {
d = (Date) obj;
tz = this.getTimeZone();
}
cal = new GregorianCalendar(tz, this.getLocale());
cal.setTime(d);
}
return toAppendTo.append(factory.newXMLGregorianCalendar(cal).toXMLFormat());
}
 
59,7 → 71,9
try {
final XMLGregorianCalendar res = factory.newXMLGregorianCalendar(source.substring(pos.getIndex()));
pos.setIndex(source.length());
return res.toGregorianCalendar().getTime();
// pass time zone if source lacks it
final TimeZone tz = res.getTimezone() == DatatypeConstants.FIELD_UNDEFINED ? this.tz : null;
return res.toGregorianCalendar(tz, this.locale, null).getTime();
} catch (Exception e) {
e.printStackTrace();
pos.setErrorIndex(pos.getIndex());
/trunk/OpenConcerto/src/org/openconcerto/utils/CollectionUtils.java
863,7 → 863,8
}
 
public static <K, V> Map<K, V> createMap(K key, V val, K key2, V val2) {
final HashMap<K, V> res = new HashMap<K, V>();
// arguments are ordered, so should the result
final Map<K, V> res = new LinkedHashMap<K, V>();
res.put(key, val);
res.put(key2, val2);
return res;
898,8 → 899,23
* @return the passed map.
*/
public static <K, V, M extends Map<K, V>> M fillMap(final M m, Collection<? extends K> keys) {
return fillMap(m, keys, null);
}
 
/**
* Fills a map with the same value.
*
* @param <K> type of key.
* @param <V> type of value.
* @param <M> type of map.
* @param m the map to fill.
* @param keys the keys to add.
* @param v the value to put.
* @return the passed map.
*/
public static <K, V, M extends Map<K, V>> M fillMap(final M m, final Collection<? extends K> keys, final V val) {
for (final K key : keys)
m.put(key, null);
m.put(key, val);
return m;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/OSFamily.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.utils;
 
public abstract class OSFamily {
 
static public class Unix extends OSFamily {
}
 
static public final Unix Mac = new Unix();
static public final Unix Linux = new Unix();
static public final Unix FreeBSD = new Unix();
static public final OSFamily Windows = new OSFamily() {
};
 
static private final OSFamily INSTANCE = getCurrentOS();
 
// perhaps create an OS class to which we pass the os.* properties
static private final OSFamily getCurrentOS() {
final String os = System.getProperty("os.name");
if (os.startsWith("Windows")) {
return Windows;
} else if (os.startsWith("Mac OS")) {
return Mac;
} else if (os.startsWith("Linux")) {
return Linux;
} else if (os.startsWith("FreeBSD")) {
return FreeBSD;
} else {
System.err.println("Unsupported OS " + os);
return null;
}
}
 
public final static OSFamily getInstance() {
return INSTANCE;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/JImage.java
62,6 → 62,7
public JImage(Image img) {
this.image = img;
this.icon = null;
this.setOpaque(true);
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
94,18 → 95,21
}
}
 
@Override
protected void paintComponent(Graphics g) {
if (this.isOpaque()) {
g.setColor(this.getBackground());
int imageW = this.image.getWidth(null);
if (!centered) {
g.fillRect(imageW, 0, this.getBounds().width - imageW, this.getBounds().height);
g.drawImage(this.image, 0, 0, null);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
final int dx;
final int dy;
if (!this.centered) {
dx = dy = 0;
} else {
int dx = (this.getBounds().width - imageW) / 2;
g.fillRect(0, 0, dx, this.getBounds().height);
g.fillRect(0, 0, dx + imageW, this.getBounds().height);
g.drawImage(this.image, dx, 0, null);
dx = (this.getWidth() - this.image.getWidth(null)) / 2;
dy = (this.getHeight() - this.image.getHeight(null)) / 2;
}
g.drawImage(this.image, dx, dy, null);
}
 
public ImageIcon getImageIcon() {
/trunk/OpenConcerto/src/org/openconcerto/utils/FileUtils.java
13,6 → 13,7
package org.openconcerto.utils;
 
import org.openconcerto.utils.OSFamily.Unix;
import org.openconcerto.utils.StringUtils.Escaper;
import org.openconcerto.utils.cc.ExnTransformer;
import org.openconcerto.utils.cc.IClosure;
93,6 → 94,13
}
}
 
// immutable, getAbsoluteFile() required otherwise list() returns null
static private final File WD = new File("").getAbsoluteFile();
 
static public final File getWD() {
return WD;
}
 
/**
* All the files (see {@link File#isFile()}) contained in the passed dir.
*
707,10 → 715,9
* @throws IOException if an error occurs.
*/
public static final File ln(final File target, final File link) throws IOException {
final String os = System.getProperty("os.name");
final Process ps;
final File res;
if (os.startsWith("Windows")) {
if (OSFamily.getInstance() == OSFamily.Windows) {
// using the .vbs since it doesn't depends on cygwin
// and cygwin's ln is weird :
// 1. needs CYGWIN=winsymlinks to create a shortcut, but even then "ln -f" doesn't work
747,9 → 754,8
* @throws IOException if an error occurs.
*/
public static final File readlink(final File link) throws IOException {
final String os = System.getProperty("os.name");
final Process ps;
if (os.startsWith("Windows")) {
if (OSFamily.getInstance() == OSFamily.Windows) {
ps = Runtime.getRuntime().exec(new String[] { "cscript", "//NoLogo", getShortCutFile().getAbsolutePath(), link.getAbsolutePath() });
} else {
// add -f to canonicalize
838,13 → 844,13
* @throws IOException if f couldn't be opened.
*/
private static final void openNative(File f) throws IOException {
final String os = System.getProperty("os.name");
final OSFamily os = OSFamily.getInstance();
final String[] cmdarray;
if (os.startsWith("Windows")) {
if (os == OSFamily.Windows) {
cmdarray = new String[] { "cmd", "/c", "start", "\"\"", f.getCanonicalPath() };
} else if (os.startsWith("Mac OS")) {
} else if (os == OSFamily.Mac) {
cmdarray = new String[] { "open", f.getCanonicalPath() };
} else if (os.startsWith("Linux")) {
} else if (os instanceof Unix) {
cmdarray = new String[] { "xdg-open", f.getCanonicalPath() };
} else {
throw new IOException("unknown way to open " + f);
/trunk/OpenConcerto/src/org/openconcerto/utils/StringInputStream.java
15,6 → 15,7
 
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
 
/**
* Permet de créer un InputStream depuis une String.
27,6 → 28,10
super(s.getBytes());
}
 
public StringInputStream(String s, Charset charset) {
super(s.getBytes(charset));
}
 
public StringInputStream(String s, String charset) throws UnsupportedEncodingException {
super(s.getBytes(charset));
}
/trunk/OpenConcerto/src/org/openconcerto/utils/XMLCalendarFormat.java
New file
0,0 → 1,92
/*
* 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;
 
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
 
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
 
/**
* Format a {@link Calendar} local time according to <a
* href="http://www.w3.org/TR/xmlschema-2/#dateTime">W3C XML Schema 1.0 Part 2, Section
* 3.2.7-14</a>. I.e. {@link #format(Object)} never write a time zone part and
* {@link #parseObject(String)} will always use this {@link #getTimeZone() time zone}.
*
* @author Sylvain CUAZ
* @see XMLGregorianCalendar
* @see XMLDateFormat
*/
public class XMLCalendarFormat extends AbstractXMLDateFormat {
 
private final boolean alwaysParseGregorian;
 
public XMLCalendarFormat() {
this(null, null);
}
 
public XMLCalendarFormat(final TimeZone timezone, final Locale aLocale) {
this(timezone, aLocale, false);
}
 
public XMLCalendarFormat(final TimeZone timezone, final Locale aLocale, final boolean alwaysParseGregorian) {
super(timezone, aLocale);
this.alwaysParseGregorian = alwaysParseGregorian;
}
 
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
final GregorianCalendar cal;
if (obj instanceof Date) {
cal = new GregorianCalendar(getTimeZone(), getLocale());
cal.setTime((Date) obj);
} else if (obj instanceof GregorianCalendar) {
cal = (GregorianCalendar) obj;
} else {
final Calendar nonGregCal = (Calendar) obj;
cal = new GregorianCalendar(nonGregCal.getTimeZone());
cal.setTime(nonGregCal.getTime());
}
final XMLGregorianCalendar xmlCal = factory.newXMLGregorianCalendar(cal);
xmlCal.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
return toAppendTo.append(xmlCal.toXMLFormat());
}
 
@Override
public Calendar parseObject(String source, ParsePosition pos) {
try {
final XMLGregorianCalendar xmlCal = factory.newXMLGregorianCalendar(source.substring(pos.getIndex()));
pos.setIndex(source.length());
final GregorianCalendar gregorianCalendar = xmlCal.toGregorianCalendar(getTimeZone(), getLocale(), null);
final Calendar res;
if (this.alwaysParseGregorian) {
res = gregorianCalendar;
} else {
res = Calendar.getInstance(getTimeZone(), getLocale());
res.setTime(gregorianCalendar.getTime());
}
return res;
} catch (Exception e) {
e.printStackTrace();
pos.setErrorIndex(pos.getIndex());
return null;
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/GestionDevise.java
97,14 → 97,12
if (decpl == -1 || decpl == 0) {
num *= 100;
} else {
// FIXME
if (decpl == 2) {
/* it is fine as is */
} else {
ExceptionHandler.handle(numStr + " wrong number of decimal places.");
return 0;
if (decpl > 2) {
for (int i = 2; i < decpl; i++) {
num /= 10;
}
}
}
if (negative) {
num = -num;
}
/trunk/OpenConcerto/src/org/openconcerto/utils/CollectionMap2.java
14,11 → 14,14
package org.openconcerto.utils;
 
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
 
/**
63,6 → 66,7
 
private final boolean emptyCollSameAsNoColl;
private final Mode mode;
private Collection<V> allValues = null;
 
public CollectionMap2() {
this(DEFAULT_MODE);
149,7 → 153,95
return this.get(key, !this.isEmptyCollSameAsNoColl(), true);
}
 
/**
* Returns a {@link Collection} view of all the values contained in this map. The collection is
* backed by the map, so changes to the map are reflected in the collection, and vice-versa. If
* the map is modified while an iteration over the collection is in progress (except through the
* iterator's own <tt>remove</tt> operation), the results of the iteration are undefined. The
* collection supports element removal, which removes the corresponding values from the map, via
* the <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, <tt>removeAll</tt>,
* <tt>retainAll</tt> and <tt>clear</tt> operations. Note that it doesn't remove entries only
* values : keySet() doesn't change, use {@link #removeAllEmptyCollections()} and
* {@link #removeAllNullCollections()} afterwards. It does not support the <tt>add</tt> or
* <tt>addAll</tt> operations.
*
* @return a view all values in all entries, <code>null</code> collections are ignored.
*/
public Collection<V> allValues() {
if (this.allValues == null)
this.allValues = new AllValues();
return this.allValues;
}
 
private final class AllValues extends AbstractCollection<V> {
@Override
public Iterator<V> iterator() {
return new AllValuesIterator();
}
 
@Override
public boolean isEmpty() {
return !iterator().hasNext();
}
 
@Override
public int size() {
int compt = 0;
final Iterator<V> it = iterator();
while (it.hasNext()) {
it.next();
compt++;
}
return compt;
}
 
// don't overload clear() to call Map.clear() as this would be incoherent with removeAll() :
// this last method only removes values, resulting in empty and null collections
}
 
private final class AllValuesIterator implements Iterator<V> {
private final Iterator<C> mapIterator;
private Iterator<V> tempIterator;
 
private AllValuesIterator() {
this.mapIterator = values().iterator();
this.tempIterator = null;
}
 
private boolean searchNextIterator() {
// tempIterator == null initially and when a collection is null
while (this.tempIterator == null || !this.tempIterator.hasNext()) {
if (!this.mapIterator.hasNext()) {
return false;
}
final C nextCol = this.mapIterator.next();
this.tempIterator = nextCol == null ? null : nextCol.iterator();
}
return true;
}
 
@Override
public boolean hasNext() {
return searchNextIterator();
}
 
@Override
public V next() {
// search next iterator if necessary
if (!hasNext())
throw new NoSuchElementException();
return this.tempIterator.next();
}
 
@Override
public void remove() {
if (this.tempIterator == null)
throw new IllegalStateException();
this.tempIterator.remove();
}
}
 
@Override
public Set<Map.Entry<K, C>> entrySet() {
if (getMode() == Mode.NULL_FORBIDDEN) {
// MAYBE cache
269,6 → 361,10
this.addAll(k, Collections.singleton(v));
}
 
public final void addAll(K k, V... v) {
this.addAll(k, Arrays.asList(v));
}
 
public final void addAll(K k, Collection<? extends V> v) {
final boolean nullIsAll = getMode() == Mode.NULL_MEANS_ALL;
if (v == null && !nullIsAll)
292,6 → 388,10
}
}
 
public final void remove(K k, V v) {
this.removeAll(k, Collections.singleton(v));
}
 
public final void removeAll(K k, Collection<? extends V> v) {
this.removeAll(k, v, null);
}
356,23 → 456,27
this.remove(k);
}
 
public final void removeAllEmptyCollections() {
this.removeAll(true);
public final Set<K> removeAllEmptyCollections() {
return this.removeAll(true);
}
 
public final void removeAllNullCollections() {
this.removeAll(false);
public final Set<K> removeAllNullCollections() {
return this.removeAll(false);
}
 
private final void removeAll(final boolean emptyOrNull) {
private final Set<K> removeAll(final boolean emptyOrNull) {
final Set<K> removed = new HashSet<K>();
final Iterator<Map.Entry<K, C>> iter = this.entrySet().iterator();
while (iter.hasNext()) {
final Map.Entry<K, C> e = iter.next();
final C val = e.getValue();
if ((emptyOrNull && val != null && val.isEmpty()) || (!emptyOrNull && val == null))
if ((emptyOrNull && val != null && val.isEmpty()) || (!emptyOrNull && val == null)) {
iter.remove();
removed.add(e.getKey());
}
}
return removed;
}
 
protected abstract C createCollection(Collection<? extends V> v);
 
/trunk/OpenConcerto/src/org/openconcerto/utils/ProcessStreams.java
31,6 → 31,31
*/
public class ProcessStreams {
 
static public enum Action {
/**
* Redirect process streams to ours.
*/
REDIRECT,
/**
* Close process streams.
*/
CLOSE,
/**
* Do nothing, which is dangerous as the process will hang until its output is read.
*/
DO_NOTHING
}
 
static public final Process handle(final Process p, final Action action) throws IOException {
if (action == Action.CLOSE) {
p.getInputStream().close();
p.getErrorStream().close();
} else if (action == Action.REDIRECT) {
new ProcessStreams(p);
}
return p;
}
 
private final ExecutorService exec = Executors.newFixedThreadPool(2);
private final CountDownLatch latch;
private final Future<?> out;
40,7 → 65,7
this.latch = new CountDownLatch(2);
this.out = writeToAsync(p.getInputStream(), System.out);
this.err = writeToAsync(p.getErrorStream(), System.err);
new Thread(new Runnable() {
this.exec.submit(new Runnable() {
public void run() {
try {
ProcessStreams.this.latch.await();
51,7 → 76,7
ProcessStreams.this.exec.shutdown();
}
}
}).start();
});
}
 
protected final void stopOut() {
/trunk/OpenConcerto/src/org/openconcerto/utils/StringUtils.java
32,12 → 32,14
*/
public class StringUtils {
 
// required encoding see
// required encoding see Charset
public static final Charset UTF8 = Charset.forName("UTF-8");
public static final Charset UTF16 = Charset.forName("UTF-16");
public static final Charset ASCII = Charset.forName("US-ASCII");
public static final Charset ISO8859_1 = Charset.forName("ISO-8859-1");
// included in rt.jar see
// http://docs.oracle.com/javase/7/docs/technotes/guides/intl/encoding.doc.html
public static final Charset UTF8 = Charset.forName("UTF8");
public static final Charset UTF16 = Charset.forName("UTF-16");
public static final Charset ASCII = Charset.forName("ASCII");
public static final Charset ISO8859_1 = Charset.forName("ISO8859_1");
public static final Charset ISO8859_15 = Charset.forName("ISO-8859-15");
public static final Charset Cp1252 = Charset.forName("Cp1252");
public static final Charset Cp850 = Charset.forName("Cp850");
 
189,6 → 191,39
return res;
}
 
static public enum Side {
LEFT, RIGHT
}
 
public static String getFixedWidthString(final String s, final int width, final Side align) {
return getFixedWidthString(s, width, align, false);
}
 
public static String getFixedWidthString(final String s, final int width, final Side align, final boolean allowTruncate) {
final int length = s.length();
final String res;
if (length == width) {
res = s;
} else if (length < width) {
final StringBuilder sb = new StringBuilder(width);
if (align == Side.LEFT)
sb.append(s);
final int n = width - length;
for (int i = 0; i < n; i++) {
sb.append(' ');
}
if (align == Side.RIGHT)
sb.append(s);
res = sb.toString();
} else if (allowTruncate) {
res = s.substring(0, width);
} else {
throw new IllegalArgumentException("Too wide : " + length + " > " + width);
}
assert res.length() == width;
return res;
}
 
public static final List<String> fastSplit(final String string, final char sep) {
final List<String> l = new ArrayList<String>();
final int length = string.length();
394,4 → 429,38
}
return ((st > 0) || (end < s.length())) ? s.substring(st, end) : s;
}
 
public static String limitLength(String s, int maxLength) {
if (s.length() <= maxLength) {
return s;
}
return s.substring(0, maxLength);
}
 
public static String removeAllSpaces(String text) {
final int length = text.length();
final StringBuilder builder = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
if (c <= ' ' && c != 160) {
// remove non printable chars
// spaces
// non breakable space (160)
builder.append(c);
}
}
return builder.toString();
}
 
public static String removeNonDecimalChars(String text) {
final int length = text.length();
final StringBuilder builder = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = text.charAt(i);
if (Character.isDigit(c) || c == '.' || c == '+' || c == '-') {
builder.append(c);
}
}
return builder.toString();
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SyncClient.java
49,6 → 49,8
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
 
import sun.misc.HexDumpEncoder;
 
public class SyncClient {
private long byteSent;
private long byteReceived;
255,7 → 257,7
data += "&" + URLEncoder.encode("tk", "UTF-8") + "=" + URLEncoder.encode(token, "UTF-8");
}
this.byteSent += data.getBytes().length;
// Send data
// Send the path, file name and token
URLConnection conn = getConnection(this.baseUrl + "/getHash");
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
262,6 → 264,7
wr.write(data);
wr.flush();
wr.close();
// Get the info about the remote file
byte[] localFileHash = null;
int localFileSize = (int) localFile.length();
RangeList rangesOk = new RangeList(localFileSize);
268,8 → 271,10
MoveOperationList moves = new MoveOperationList();
this.byteSyncUpload = localFileSize;
try {
// Get the response
// Process the response
DataInputStream in = new DataInputStream(new BufferedInputStream(conn.getInputStream()));
 
// Remote file size
int remoteFileSize = in.readInt();
this.byteReceived += 4;
 
285,10 → 290,12
// System.out.print("Known hash " + r32 + " : ");
in.read(b);
this.byteReceived += 4 + 16;
 
// MD5 hash
map.put(r32, b);
//
mapBlock.put(r32, i);
}
// The hash (SHA256) of the remote file
byte[] remoteFileHash = new byte[32];
in.read(remoteFileHash);
this.byteReceived += 32;
297,12 → 304,13
if (localFileSize == remoteFileSize) {
localFileHash = HashWriter.getHash(localFile);
if (HashWriter.compareHash(localFileHash, remoteFileHash)) {
 
// Already in sync
return;
}
}
 
if (localFileSize > 0) {
 
// compare delta
RollingChecksum32 checksum = new RollingChecksum32();
byte[] buffer = new byte[HashWriter.blockSize];
352,7 → 360,7
 
} while (v >= 0);
fb.close();
 
}
} catch (FileNotFoundException e) {
// System.out.println("Sending the complete file");
}
431,7 → 439,13
}
 
public void retrieveDirectory(File dir, String remotePath, String token) throws Exception {
ArrayList<FileProperty> list = getList(remotePath, token);
 
ArrayList<FileProperty> list = null;
try {
list = getList(remotePath, token);
} catch (Exception e) {
throw new IllegalStateException("Unable to retrieve the file list of " + remotePath, e);
}
// Check locally
for (int i = 0; i < list.size(); i++) {
final FileProperty fp = list.get(i);
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/Range.java
36,9 → 36,16
}
}
 
public void setStart(int start) {
this.start = start;
if (stop <= start) {
throw new IllegalArgumentException("Invalid range " + start + " - " + stop);
}
}
 
@Override
public String toString() {
return "[" + start + "-" + stop + "]";
return "[" + start + "-" + stop + "[";
}
 
public boolean isEmpty() {
48,4 → 55,5
public int size() {
return stop - start;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/RangeList.java
24,6 → 24,9
this.limit = limit;
}
 
// Add a Range at the end of the list
// The range MUST start after the last added range
// to preserve the global order
public void add(Range range) {
if (range.getStart() < 0 || range.getStart() >= limit) {
throw new IllegalArgumentException(range + " start out of limit");
33,12 → 36,12
}
if (list.size() > 0) {
Range last = list.get(list.size() - 1);
if (last.getStop() == range.getStart()) {
last.setStop(range.getStop());
} else if (range.getStart() < last.getStart()) {
throw new IllegalArgumentException("start (" + range.getStart() + ") < lastStart (" + last.getStart() + ")");
if (last.getStart() > range.getStop() || last.getStop() < range.getStart()) {
list.add(range);
} else if (range.getStart() >= last.getStart()) {
last.setStop(Math.max(last.getStop(), range.getStop()));
} else {
list.add(range);
throw new IllegalArgumentException(range + " before " + last);
}
} else {
list.add(range);
55,16 → 58,15
result.add(new Range(0, limit));
return result;
}
 
// First
Range r = new Range(0, list.get(0).getStart());
if (!r.isEmpty()) {
// System.out.println("AddFirst:" + r);
result.add(r);
}
for (int i = 0; i < list.size() - 1; i++) {
Range r1 = this.list.get(i);
Range r2 = this.list.get(i + 1);
 
result.add(new Range(r1.getStop(), r2.getStart()));
 
}
71,7 → 73,6
// Last
Range lastRange = new Range(list.get(list.size() - 1).getStop(), limit);
if (!lastRange.isEmpty()) {
// System.out.println("AddLast:" + lastRange);
result.add(lastRange);
}
 
/trunk/OpenConcerto/src/org/openconcerto/utils/ExceptionHandler.java
138,20 → 138,16
 
private Future<?> display(final boolean error) {
final String msg = this.getMessage();
if (error) {
getLogger().log(Level.SEVERE, null, this);
} else {
if (this.getCause() != null) {
getLogger().log(Level.INFO, null, this);
}
}
// 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 (SwingUtilities.isEventDispatchThread()) {
showMsg(msg, error);
showMsgHardened(msg, error);
} else {
final FutureTask<?> run = new FutureTask<Object>(new Runnable() {
public void run() {
showMsg(msg, error);
showMsgHardened(msg, error);
}
}, null);
if (error) {
174,6 → 170,23
return this.future;
}
 
protected final void showMsgHardened(final String msg, final boolean error) {
try {
showMsg(msg, error);
} 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
// Thread.setDefaultUncaughtExceptionHandler(), it will create an infinite loop.
e = new Exception("Couldn't display message", e);
e.printStackTrace();
try {
// last ditch effort
JOptionPane.showMessageDialog(null, e.getMessage() + " : " + msg);
} catch (Throwable e2) {
}
}
}
 
protected final void showMsg(final String msg, final boolean quit) {
final JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
182,7 → 195,7
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
final JImage im = new JImage(new ImageIcon(this.getClass().getResource("error.png")));
final JImage im = new JImage(new ImageIcon(ExceptionHandler.class.getResource("error.png")));
final JLabel l = new JLabel("Une erreur est survenue");
l.setFont(l.getFont().deriveFont(Font.BOLD));
 
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/LocalizedInstances.java
69,8 → 69,8
 
/**
* Create instances of the same language. For each candidate locale, this method first looks for
* {@link #getConstructor(Class) a class} and then for {@link #createInstance(String, Locale)
* other files}.
* {@link #getInstance(Class) a class} and then for
* {@link #createInstance(String, Locale, Class) other files}.
*
* @param baseName the base name of the classes, e.g. "org.acme.MyClass".
* @param locale the desired locale, e.g. fr_FR.
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/TM.java
47,6 → 47,12
*/
public class TM {
 
static public enum MissingMode {
EXCEPTION, NULL, STRING
}
 
static private final MissingMode DEFAULT_MISSING_MODE = MissingMode.STRING;
 
static private final TM INSTANCE = new TM();
static private final Pattern splitPtrn = Pattern.compile("__", Pattern.LITERAL);
static private boolean USE_DYNAMIC_MAP = true;
135,7 → 141,7
return this.translationsLocale;
}
 
protected final String getBaseName() {
protected String getBaseName() {
return I18nUtils.getBaseName(this.getClass());
}
 
145,7 → 151,7
}
 
public final String translate(final String key, final Object... args) {
return translate(true, key, args);
return translate(DEFAULT_MISSING_MODE, key, args);
}
 
// translate map
158,22 → 164,24
}
 
public final String trM(final String key, final Map<String, ?> args) {
return trM(true, key, args);
return trM(DEFAULT_MISSING_MODE, key, args);
}
 
public final String trM(final boolean lenient, final String key, Map<String, ?> map) throws MissingResourceException {
return translate(lenient, key, new MessageArgs(map));
public final String trM(final MissingMode mode, final String key, Map<String, ?> map) throws MissingResourceException {
return translate(mode, key, new MessageArgs(map));
}
 
public final String translate(final boolean lenient, final String key, final Object... args) throws MissingResourceException {
return translate(lenient, key, new MessageArgs(args));
public final String translate(final MissingMode mode, final String key, final Object... args) throws MissingResourceException {
return translate(mode, key, new MessageArgs(args));
}
 
private final String translate(final boolean lenient, final String key, MessageArgs args) throws MissingResourceException {
private final String translate(final MissingMode mode, final String key, MessageArgs args) throws MissingResourceException {
final String res = this.translations.translate(key, args);
if (res == null) {
if (lenient)
if (mode == MissingMode.STRING)
return '!' + key + '!';
else if (mode == MissingMode.NULL)
return null;
else
throw new MissingResourceException("Missing translation", this.getBaseName(), key);
}
182,22 → 190,25
 
protected MessageArgs replaceMap(final MessageArgs args, final String msg) {
final MessageArgs res;
if (args.getAll() instanceof Map) {
if (args.isMapPrimary()) {
final Map<String, ?> map = args.getMap();
Map<String, Object> newMap;
final Map<String, Object> copy;
if (MessageArgs.isOrdered(map)) {
newMap = new LinkedHashMap<String, Object>(map);
copy = new LinkedHashMap<String, Object>(map);
} else {
newMap = new HashMap<String, Object>(map);
copy = new HashMap<String, Object>(map);
}
final Object[] array = args.getArray();
final Map<String, Object> newMap;
if (useDynamicMap()) {
newMap = new DynamicMap<Object>(newMap) {
newMap = new DynamicMap<Object>(copy) {
@Override
protected Object createValueNonNull(String key) {
return TM.this.createValue(this, key);
return TM.this.createValue(this, array, key);
}
};
} else {
newMap = copy;
final MessagePattern messagePattern = new MessagePattern(msg);
if (messagePattern.hasNamedArguments()) {
final int countParts = messagePattern.countParts();
205,7 → 216,7
for (int i = 0; i < countParts; i++) {
final Part part = messagePattern.getPart(i);
if (part.getType() == Type.ARG_NAME && !newMap.containsKey(argName = messagePattern.getSubstring(part))) {
final Object createValue = this.createValue(newMap, argName);
final Object createValue = this.createValue(newMap, array, argName);
if (createValue != null)
newMap.put(argName, createValue);
}
228,10 → 239,21
* <code>count</code> else <code>null</code>.
*
* @param map the current map.
* @param objects the original map as an array.
* @param key the missing key.
* @return its value, or <code>null</code> to leave the map unmodified.
*/
protected Object createValue(final Map<String, Object> map, final String key) {
protected Object createValue(final Map<String, Object> map, final Object[] objects, final String key) {
if (key.length() == 1 && Character.isDigit(key.charAt(0))) {
final int index = Integer.parseInt(key);
if (index < objects.length) {
return objects[index];
} else {
Log.get().warning("Only " + objects.length + " arguments : " + index);
return null;
}
}
 
final Matcher m = splitPtrn.matcher(key);
final String pattern = splitPtrn.pattern();
final int patternL = pattern.length();
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/MessageArgs.java
33,6 → 33,7
return m instanceof LinkedHashMap;
}
 
private final boolean mapPrimary;
private Object[] array;
private Map<String, ?> map;
 
48,15 → 49,21
super();
if (map == null && array == null)
throw new NullPointerException();
this.mapPrimary = map != null;
assert this.mapPrimary == (array == null);
this.array = array;
this.map = map;
}
 
public final boolean isMapPrimary() {
return this.mapPrimary;
}
public final synchronized Object getAll() {
return this.array != null ? this.array : this.map;
return this.mapPrimary ? this.map : this.array;
}
 
private synchronized Object[] getArray() {
protected synchronized Object[] getArray() {
if (this.array == null) {
this.array = new Object[this.map.size()];
int i = 0;
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/Closure.java
15,7 → 15,18
 
public abstract class Closure<E> implements IClosure<E>, ITransformer<E, Object>, org.apache.commons.collections.Closure {
 
private static final IClosure<Object> nop = new IClosure<Object>() {
@Override
public void executeChecked(Object input) {
}
};
 
@SuppressWarnings("unchecked")
public static final <N> IClosure<N> nopClosure() {
return (IClosure<N>) nop;
}
 
@SuppressWarnings("unchecked")
public final void execute(Object input) {
this.executeChecked((E) input);
}
/trunk/OpenConcerto/src/org/openconcerto/utils/IFutureTask.java
24,6 → 24,29
*/
public class IFutureTask<V> extends FutureTask<V> {
 
static private final Runnable NOOP_RUNNABLE = new Runnable() {
@Override
public void run() {
}
};
 
static public final Runnable getNoOpRunnable() {
return NOOP_RUNNABLE;
}
 
/**
* A task that does nothing and return <code>null</code>.
*
* @return a task doing nothing.
*/
static public final <V> FutureTask<V> createNoOp() {
return createNoOp(null);
}
 
static public final <V> FutureTask<V> createNoOp(final V result) {
return new IFutureTask<V>(getNoOpRunnable(), result);
}
 
private final Runnable runnable;
private final String detail;
 
/trunk/OpenConcerto/src/org/openconcerto/utils/NumberUtils.java
214,4 → 214,66
return n.doubleValue() / d;
}
}
 
static public Number negate(Number n) {
if (n == null)
return null;
final Number res;
final Class<? extends Number> clazz = n.getClass();
if (n instanceof BigDecimal) {
res = ((BigDecimal) n).negate();
} else if (n instanceof BigInteger) {
res = ((BigInteger) n).negate();
} else if (clazz == Short.class) {
// cast needed since '-' widens to int
res = (short) -n.shortValue();
} else if (clazz == Integer.class) {
res = -n.intValue();
} else if (clazz == Long.class) {
res = -n.longValue();
} else if (clazz == Byte.class) {
// cast needed since '-' widens to int
res = (byte) -n.byteValue();
} else if (clazz == AtomicInteger.class) {
res = new AtomicInteger(-n.intValue());
} else if (clazz == AtomicLong.class) {
res = new AtomicLong(-n.longValue());
} else if (clazz == Double.class) {
res = -n.doubleValue();
} else if (clazz == Float.class) {
res = -n.floatValue();
} else {
// fallback for unknown class
res = new BigDecimal(n.toString()).negate();
}
return res;
}
 
static public int signum(Number n) {
if (n == null)
throw new NullPointerException();
final int res;
final Class<? extends Number> clazz = n.getClass();
if (n instanceof BigDecimal) {
res = ((BigDecimal) n).signum();
} else if (n instanceof BigInteger) {
res = ((BigInteger) n).signum();
} else if (clazz == Double.class) {
res = (int) Math.signum(n.doubleValue());
} else if (clazz == Float.class) {
res = (int) Math.signum(n.floatValue());
} else if (clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class || clazz == AtomicInteger.class || clazz == AtomicLong.class) {
final long l = n.longValue();
if (l == 0)
res = 0;
else if (l < 0)
res = -1;
else
res = 1;
} else {
// limit overflow for unknown class
res = (int) Math.signum(n.doubleValue());
}
return res;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/TimeUtils.java
15,16 → 15,61
 
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.DatatypeConstants.Field;
 
import net.jcip.annotations.Immutable;
 
public class TimeUtils {
static private DatatypeFactory typeFactory = null;
static private List<Field> FIELDS_LIST = Arrays.asList(DatatypeConstants.YEARS, DatatypeConstants.MONTHS, DatatypeConstants.DAYS, DatatypeConstants.HOURS, DatatypeConstants.MINUTES,
DatatypeConstants.SECONDS);
static private List<Field> DATE_FIELDS, TIME_FIELDS;
 
static {
final int dayIndex = FIELDS_LIST.indexOf(DatatypeConstants.DAYS);
DATE_FIELDS = Collections.unmodifiableList(FIELDS_LIST.subList(0, dayIndex + 1));
TIME_FIELDS = Collections.unmodifiableList(FIELDS_LIST.subList(dayIndex + 1, FIELDS_LIST.size()));
}
 
public static List<Field> getAllFields() {
return FIELDS_LIST;
}
 
/**
* Get the fields for the date part.
*
* @return fields until {@link DatatypeConstants#DAYS} included.
*/
public static List<Field> getDateFields() {
return DATE_FIELDS;
}
 
/**
* Get the fields for the time part.
*
* @return fields from {@link DatatypeConstants#HOURS}.
*/
public static List<Field> getTimeFields() {
return TIME_FIELDS;
}
 
private static Class<? extends Number> getFieldClass(final Field f) {
return f == DatatypeConstants.SECONDS ? BigDecimal.class : BigInteger.class;
}
 
static public final DatatypeFactory getTypeFactory() {
if (typeFactory == null)
try {
35,6 → 80,30
return typeFactory;
}
 
static private final <N extends Number> N getZeroIfNull(final Number n, final Class<N> clazz) {
final Number res;
if (n != null)
res = n;
else if (clazz == BigInteger.class)
res = BigInteger.ZERO;
else if (clazz == BigDecimal.class)
res = BigDecimal.ZERO;
else
throw new IllegalArgumentException("Unknown class : " + n);
return clazz.cast(res);
}
 
static private final <N extends Number> N getNullIfZero(final N n) {
if (n == null)
return null;
final boolean isZero;
if (n instanceof BigInteger)
isZero = n.intValue() == 0;
else
isZero = ((BigDecimal) n).compareTo(BigDecimal.ZERO) == 0;
return isZero ? null : n;
}
 
/**
* Get non-null seconds with the the correct class.
*
44,8 → 113,7
* @see Duration#getMinutes()
*/
static public final BigDecimal getSeconds(final Duration d) {
final BigDecimal res = (BigDecimal) d.getField(DatatypeConstants.SECONDS);
return res != null ? res : BigDecimal.ZERO;
return getZeroIfNull(d.getField(DatatypeConstants.SECONDS), BigDecimal.class);
}
 
/**
60,7 → 128,110
seconds);
}
 
// removes explicit 0
public final static Duration trimDuration(final Duration dur) {
return DurationNullsChanger.ALL_TO_NULL.apply(dur);
}
 
// replace null by 0
public final static Duration removeNulls(final Duration dur) {
return DurationNullsChanger.NONE_TO_NULL.apply(dur);
}
 
public static enum EmptyFieldPolicy {
AS_IS, SET_TO_NULL, SET_TO_ZERO
}
 
public final static class DurationNullsBuilder {
 
private final Map<Field, EmptyFieldPolicy> policy;
 
public DurationNullsBuilder() {
this(EmptyFieldPolicy.AS_IS);
}
 
public DurationNullsBuilder(final EmptyFieldPolicy initialPolicy) {
this.policy = new HashMap<Field, EmptyFieldPolicy>();
this.setPolicy(FIELDS_LIST, initialPolicy);
}
 
public final void setPolicy(Collection<Field> fields, final EmptyFieldPolicy to) {
for (final Field f : fields)
this.policy.put(f, to);
}
 
public final DurationNullsBuilder setToNull(Collection<Field> fields) {
setPolicy(fields, EmptyFieldPolicy.SET_TO_NULL);
return this;
}
 
public final DurationNullsBuilder setToZero(Collection<Field> fields) {
setPolicy(fields, EmptyFieldPolicy.SET_TO_ZERO);
return this;
}
 
public final DurationNullsBuilder dontChange(Collection<Field> fields) {
setPolicy(fields, EmptyFieldPolicy.AS_IS);
return this;
}
 
public final DurationNullsChanger build() {
return new DurationNullsChanger(this.policy);
}
}
 
/**
* Allow to change empty fields between two equivalent state. In a {@link Duration} an empty
* field can be set to <code>null</code> and it won't be output or it can be set to 0 and it
* will be explicitly output.
*
* @author Sylvain
* @see DurationNullsBuilder
*/
@Immutable
public final static class DurationNullsChanger {
 
public final static DurationNullsChanger ALL_TO_NULL = new DurationNullsBuilder(EmptyFieldPolicy.SET_TO_NULL).build();
public final static DurationNullsChanger NONE_TO_NULL = new DurationNullsBuilder(EmptyFieldPolicy.SET_TO_ZERO).build();
 
private final Map<Field, EmptyFieldPolicy> policy;
 
private DurationNullsChanger(final Map<Field, EmptyFieldPolicy> policy) {
this.policy = Collections.unmodifiableMap(new HashMap<Field, EmptyFieldPolicy>(policy));
}
 
// doesn't change the duration value, just nulls and 0s
public final Duration apply(final Duration dur) {
boolean changed = false;
final Map<Field, Number> newValues = new HashMap<Field, Number>();
for (final Field f : FIELDS_LIST) {
final Number oldVal = dur.getField(f);
final EmptyFieldPolicy pol = this.policy.get(f);
final Number newVal;
if (pol == EmptyFieldPolicy.SET_TO_NULL) {
newVal = getNullIfZero(oldVal);
} else if (pol == EmptyFieldPolicy.SET_TO_ZERO) {
newVal = getZeroIfNull(oldVal, getFieldClass(f));
} else {
assert pol == EmptyFieldPolicy.AS_IS;
newVal = oldVal;
}
newValues.put(f, newVal);
changed |= !CompareUtils.equals(newVal, oldVal);
}
 
if (!changed) {
// Duration is immutable
return dur;
} else {
return getTypeFactory().newDuration(dur.getSign() >= 0, (BigInteger) newValues.get(DatatypeConstants.YEARS), (BigInteger) newValues.get(DatatypeConstants.MONTHS),
(BigInteger) newValues.get(DatatypeConstants.DAYS), (BigInteger) newValues.get(DatatypeConstants.HOURS), (BigInteger) newValues.get(DatatypeConstants.MINUTES),
(BigDecimal) newValues.get(DatatypeConstants.SECONDS));
}
}
}
 
/**
* Normalize <code>cal</code> so that any Calendar with the same local time have the same
* result. If you don't need a Calendar this is faster than
* {@link #copyLocalTime(Calendar, Calendar)}.
75,16 → 246,42
/**
* Copy the local time from one calendar to another. Except if both calendars have the same time
* zone, from.getTimeInMillis() will be different from to.getTimeInMillis().
* <p>
* NOTE : In case the two calendars are not from the same class but one of them is a
* {@link GregorianCalendar} then this method will use a GregorianCalendar with the time zone
* and absolute time of the other.
* </p>
*
* @param from the source calendar, e.g. 23/12/2011 11:55:33.066 GMT-12.
* @param to the destination calendar, e.g. 01/01/2000 0:00 GMT+13.
* @return the modified destination calendar, e.g. 23/12/2011 11:55:33.066 GMT+13.
* @throws IllegalArgumentException if both calendars aren't from the same class and none of
* them are Gregorian.
*/
public final static Calendar copyLocalTime(final Calendar from, final Calendar to) {
to.clear();
for (final int field : new int[] { Calendar.YEAR, Calendar.DAY_OF_YEAR, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) {
to.set(field, from.get(field));
public final static Calendar copyLocalTime(final Calendar from, final Calendar to) throws IllegalArgumentException {
final boolean sameClass = from.getClass() == to.getClass();
final boolean createGregSource = !sameClass && to.getClass() == GregorianCalendar.class;
final boolean createGregDest = !sameClass && from.getClass() == GregorianCalendar.class;
if (!sameClass && !createGregSource && !createGregDest)
throw new IllegalArgumentException("Calendars mismatch " + from.getClass() + " != " + to.getClass());
 
final Calendar source = createGregSource ? new GregorianCalendar(from.getTimeZone()) : from;
if (createGregSource) {
source.setTime(from.getTime());
}
final Calendar dest = createGregDest ? new GregorianCalendar(to.getTimeZone()) : to;
assert source.getClass() == dest.getClass();
if (source.getTimeZone().equals(dest.getTimeZone())) {
dest.setTimeInMillis(source.getTimeInMillis());
} else {
dest.clear();
for (final int field : new int[] { Calendar.ERA, Calendar.YEAR, Calendar.DAY_OF_YEAR, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) {
dest.set(field, source.get(field));
}
}
if (createGregDest) {
to.setTime(dest.getTime());
}
return to;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/TableSorter.java
111,6 → 111,7
private Map columnComparators = new HashMap();
private List sortingColumns = new ArrayList();
 
private boolean enabled;
private boolean sorting;
private final PropertyChangeSupport supp;
 
117,6 → 118,7
public TableSorter() {
this.mouseListener = new MouseHandler();
this.tableModelListener = new TableModelHandler();
this.enabled = true;
this.sorting = false;
this.supp = new PropertyChangeSupport(this);
}
158,6 → 160,29
fireTableStructureChanged();
}
 
public final void setSortingEnabled(final boolean b) {
this.setSortingEnabled(b, true);
}
 
// this prevent the user from changing the sort, but setSortingStatus() still works (like
// JTextComponent.setEnabled()/setText())
public final void setSortingEnabled(final boolean b, final boolean cancelSort) {
if (this.enabled != b) {
this.enabled = b;
if (this.enabled) {
this.tableHeader.addMouseListener(mouseListener);
} else {
this.tableHeader.removeMouseListener(mouseListener);
}
if (cancelSort && this.isSorting())
this.cancelSorting(true);
}
}
 
public final boolean isSortingEnabled() {
return this.enabled;
}
 
public JTableHeader getTableHeader() {
return tableHeader;
}
172,6 → 197,7
}
this.tableHeader = tableHeader;
if (this.tableHeader != null) {
if (this.isSortingEnabled())
this.tableHeader.addMouseListener(mouseListener);
this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
}
/trunk/OpenConcerto/src/org/openconcerto/utils/AbstractXMLDateFormat.java
New file
0,0 → 1,53
/*
* 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;
 
import java.text.Format;
import java.util.Locale;
import java.util.TimeZone;
 
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
 
abstract class AbstractXMLDateFormat extends Format {
 
// thread-safe since state-less
protected final static DatatypeFactory factory;
static {
try {
factory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
// shouldn't happen since an implementation is provided with the JRE
throw new IllegalStateException(e);
}
}
 
protected final TimeZone tz;
protected final Locale locale;
 
// null means VM default
protected AbstractXMLDateFormat(final TimeZone timezone, final Locale aLocale) {
this.tz = timezone == null ? null : (TimeZone) timezone.clone();
// immutable
this.locale = aLocale;
}
 
protected final TimeZone getTimeZone() {
return this.tz == null ? TimeZone.getDefault() : this.tz;
}
 
protected final Locale getLocale() {
return this.locale == null ? Locale.getDefault() : this.locale;
}
}
/trunk/OpenConcerto/src/org/openconcerto/xml/XMLCodecUtils.java
44,10 → 44,10
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.RandomAccess;
import java.util.Set;
import java.util.Stack;
import java.util.Map.Entry;
 
import org.jdom.Element;
import org.jdom.JDOMException;
75,7 → 75,10
private static PersistenceDelegate defaultPersistenceDelegate = new DefaultPersistenceDelegate();
private static final Map<Class, PersistenceDelegate> persDelegates = new HashMap<Class, PersistenceDelegate>();
 
private static final ExceptionListener EXCEPTION_LISTENER = new ExceptionListener() {
/**
* Just throws an {@link IllegalStateException}.
*/
public static final ExceptionListener EXCEPTION_LISTENER = new ExceptionListener() {
@Override
public void exceptionThrown(Exception e) {
throw new IllegalStateException(e);
/trunk/OpenConcerto/src/org/openconcerto/laf/IScrollBarUI.java
82,8 → 82,8
scrollBarWidth = 13;
super.installDefaults();
scrollbar.setBorder(null);
this.h = new ImageIcon(this.getClass().getResource("scrollHaut.png")).getImage();
this.b = new ImageIcon(this.getClass().getResource("scrollBas.png")).getImage();
this.h = new ImageIcon(IScrollBarUI.class.getResource("scrollHaut.png")).getImage();
this.b = new ImageIcon(IScrollBarUI.class.getResource("scrollBas.png")).getImage();
 
}
 
/trunk/OpenConcerto/src/org/openconcerto/laf/IComboBoxUI.java
14,33 → 14,20
package org.openconcerto.laf;
 
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
 
import javax.swing.ComboBoxEditor;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxButton;
import javax.swing.plaf.metal.MetalComboBoxEditor;
import javax.swing.plaf.metal.MetalComboBoxUI;
import javax.swing.plaf.metal.MetalLookAndFeel;
 
import javax.swing.plaf.metal.MetalComboBoxUI.MetalComboBoxLayoutManager;
 
public class IComboBoxUI extends MetalComboBoxUI {
 
static int comboBoxButtonSize = 19;
49,16 → 36,6
return new IComboBoxUI();
}
 
/*public void paint(Graphics g, JComponent c) {
}*/
 
/*protected ComboBoxEditor createEditor() {
return new MetalComboBoxEditor.UIResource();
}
 
protected ComboPopup createPopup() {
return new BasicComboPopup(comboBox);
}*/
// This is here because of a bug in the compiler.
// When a protected-inner-class-savvy compiler comes out we
// should move this into MetalComboBoxLayoutManager.
68,23 → 45,15
Icon icon = arrowButton.getIcon();
Insets buttonInsets = arrowButton.getInsets();
Insets insets = comboBox.getInsets();
int buttonWidth = icon.getIconWidth() + buttonInsets.left +
buttonInsets.right;
arrowButton.setBounds( (comboBox.getWidth() - insets.right - buttonWidth)
,
insets.top, buttonWidth,
comboBox.getHeight() - insets.top - insets.bottom);
}
else {
int buttonWidth = icon.getIconWidth() + buttonInsets.left + buttonInsets.right;
arrowButton.setBounds((comboBox.getWidth() - insets.right - buttonWidth), insets.top, buttonWidth, comboBox.getHeight() - insets.top - insets.bottom);
} else {
Insets insets = comboBox.getInsets();
int width = comboBox.getWidth();
int height = comboBox.getHeight();
arrowButton.setBounds( insets.left, insets.top,
width - (insets.left + insets.right),
height - (insets.top + insets.bottom) );
arrowButton.setBounds(insets.left, insets.top, width - (insets.left + insets.right), height - (insets.top + insets.bottom));
}
 
if (editor != null /*&& MetalLookAndFeel.usingOcean()*/) {
Rectangle cvb = rectangleForCurrentValue();
editor.setBounds(cvb);
95,8 → 64,7
//JButton button = new XPComboBoxButton(comboBox, //
// new MetalComboBoxIcon(), comboBox.isEditable(), currentValuePane, listBox);
JButton button=new JButton(new ImageIcon(this.getClass().getResource("comboright.png")));
JButton button = new JButton(new ImageIcon(IComboBoxUI.class.getResource("comboright.png")));
button.setBackground(new Color(239, 235, 231));
button.setMargin(new Insets(0, 0, 0, 0));
111,9 → 79,9
}
 
/**
* This inner class is marked &quot;public&quot; due to a compiler bug. This
* class should be treated as a &quot;protected&quot; inner class.
* Instantiate it only within subclasses of <FooUI>.
* This inner class is marked &quot;public&quot; due to a compiler bug. This class should be
* treated as a &quot;protected&quot; inner class. Instantiate it only within subclasses of
* <FooUI>.
*/
public class XPPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler {
public void propertyChange(PropertyChangeEvent e) {
136,9 → 104,8
}
 
/**
* As of Java 2 platform v1.4 this method is no longer used. Do not call or
* override. All the functionality of this method is in the
* MetalPropertyChangeListener.
* As of Java 2 platform v1.4 this method is no longer used. Do not call or override. All the
* functionality of this method is in the MetalPropertyChangeListener.
*
* @deprecated As of Java 2 platform v1.4.
*/
145,127 → 112,81
protected void editablePropertyChanged(PropertyChangeEvent e) {
}
 
/*protected LayoutManager createLayoutManager() {
return new MetouiaComboBoxLayoutManager();
}*/
/*
* protected LayoutManager createLayoutManager() { return new MetouiaComboBoxLayoutManager(); }
*/
 
/**
* This inner class is marked &quot;public&quot; due to a compiler bug. This
* class should be treated as a &quot;protected&quot; inner class.
* Instantiate it only within subclasses of <FooUI>.
* This inner class is marked &quot;public&quot; due to a compiler bug. This class should be
* treated as a &quot;protected&quot; inner class. Instantiate it only within subclasses of
* <FooUI>.
*/
/*public class MetouiaComboBoxLayoutManager implements LayoutManager {
public void addLayoutComponent(String name, Component comp) {
}
 
public void removeLayoutComponent(Component comp) {
}
 
public Dimension preferredLayoutSize(Container parent) {
JComboBox cb = (JComboBox) parent;
return parent.getPreferredSize();
}
 
public Dimension minimumLayoutSize(Container parent) {
JComboBox cb = (JComboBox) parent;
return parent.getMinimumSize();
}
 
public void layoutContainer(Container parent) {
JComboBox cb = (JComboBox) parent;
int width = cb.getWidth();
int height = cb.getHeight();
 
Rectangle cvb;
 
if (comboBox.isEditable()) {
if (arrowButton != null) {
arrowButton.setBounds(width - comboBoxButtonSize, 0, comboBoxButtonSize, height);
}
if (editor != null) {
cvb = rectangleForCurrentValue2();
editor.setBounds(cvb);
}
} else {
arrowButton.setBounds(0, 0, width, height);
}
}
}
 
protected Rectangle rectangleForCurrentValue2() {
int width = comboBox.getWidth();
int height = comboBox.getHeight();
Insets insets = getInsets();
int buttonSize = height - (insets.top + insets.bottom);
if (arrowButton != null) {
buttonSize = comboBoxButtonSize;
}
if (comboBox.getComponentOrientation().isLeftToRight()) {
return new Rectangle(insets.left, insets.top, width - (insets.left + insets.right + buttonSize), height - (insets.top + insets.bottom));
} else {
return new Rectangle(insets.left + buttonSize, insets.top, width - (insets.left + insets.right + buttonSize), height - (insets.top + insets.bottom));
}
}
 
protected void removeListeners() {
if (propertyChangeListener != null) {
comboBox.removePropertyChangeListener(propertyChangeListener);
}
}
 
// These two methods were overloaded and made public. This was probably a
// mistake in the implementation. The functionality that they used to
// provide is no longer necessary and should be removed. However,
// removing them will create an uncompatible API change.
 
public void configureEditor() {
super.configureEditor();
}
 
public void unconfigureEditor() {
super.unconfigureEditor();
}
 
public Dimension getMinimumSize(JComponent c) {
if (!isMinimumSizeDirty) {
return new Dimension(cachedMinimumSize);
}
 
Dimension size = null;
 
if (!comboBox.isEditable() && arrowButton != null ) {
 
JButton button = arrowButton;
Insets buttonInsets = new Insets(0, 0, 0, 0);
Insets insets = comboBox.getInsets();
 
size = getDisplaySize();
size.width += comboBoxButtonSize + insets.left + insets.right; // Hack
size.width += buttonInsets.left + buttonInsets.right;
size.width += buttonInsets.right + 9;// FIXME button.getComboIcon().getIconWidth();
size.height += insets.top + insets.bottom;
size.height += buttonInsets.top + buttonInsets.bottom;
size.height = Math.max(21, size.height);
} else if (comboBox.isEditable() && arrowButton != null && editor != null) {
size = super.getMinimumSize(c);
Insets margin = arrowButton.getMargin();
Insets insets = comboBox.getInsets();
if (editor instanceof JComponent) {
Insets editorInsets = ((JComponent) editor).getInsets();
}
size.height += margin.top + margin.bottom;
size.height += insets.top + insets.bottom;
 
// size.height = Math.max(20,size.height);
} else {
size = super.getMinimumSize(c);
}
 
cachedMinimumSize.setSize(size.width, size.height);
isMinimumSizeDirty = false;
 
return new Dimension(cachedMinimumSize);
}
/*
* public class MetouiaComboBoxLayoutManager implements LayoutManager { public void
* addLayoutComponent(String name, Component comp) { }
*
* public void removeLayoutComponent(Component comp) { }
*
* public Dimension preferredLayoutSize(Container parent) { JComboBox cb = (JComboBox) parent;
* return parent.getPreferredSize(); }
*
* public Dimension minimumLayoutSize(Container parent) { JComboBox cb = (JComboBox) parent;
* return parent.getMinimumSize(); }
*
* public void layoutContainer(Container parent) { JComboBox cb = (JComboBox) parent; int width
* = cb.getWidth(); int height = cb.getHeight();
*
* Rectangle cvb;
*
* if (comboBox.isEditable()) { if (arrowButton != null) { arrowButton.setBounds(width -
* comboBoxButtonSize, 0, comboBoxButtonSize, height); } if (editor != null) { cvb =
* rectangleForCurrentValue2(); editor.setBounds(cvb); } } else { arrowButton.setBounds(0, 0,
* width, height); } } }
*
* protected Rectangle rectangleForCurrentValue2() { int width = comboBox.getWidth(); int height
* = comboBox.getHeight(); Insets insets = getInsets(); int buttonSize = height - (insets.top +
* insets.bottom); if (arrowButton != null) { buttonSize = comboBoxButtonSize; } if
* (comboBox.getComponentOrientation().isLeftToRight()) { return new Rectangle(insets.left,
* insets.top, width - (insets.left + insets.right + buttonSize), height - (insets.top +
* insets.bottom)); } else { return new Rectangle(insets.left + buttonSize, insets.top, width -
* (insets.left + insets.right + buttonSize), height - (insets.top + insets.bottom)); } }
*
*
* protected void removeListeners() { if (propertyChangeListener != null) {
* comboBox.removePropertyChangeListener(propertyChangeListener); } }
*
* // These two methods were overloaded and made public. This was probably a // mistake in the
* implementation. The functionality that they used to // provide is no longer necessary and
* should be removed. However, // removing them will create an uncompatible API change.
*
* public void configureEditor() { super.configureEditor(); }
*
* public void unconfigureEditor() { super.unconfigureEditor(); }
*
* public Dimension getMinimumSize(JComponent c) { if (!isMinimumSizeDirty) { return new
* Dimension(cachedMinimumSize); }
*
* Dimension size = null;
*
* if (!comboBox.isEditable() && arrowButton != null ) {
*
* JButton button = arrowButton; Insets buttonInsets = new Insets(0, 0, 0, 0); Insets insets =
* comboBox.getInsets();
*
* size = getDisplaySize(); size.width += comboBoxButtonSize + insets.left + insets.right; //
* Hack size.width += buttonInsets.left + buttonInsets.right; size.width += buttonInsets.right +
* 9;// FIXME button.getComboIcon().getIconWidth(); size.height += insets.top + insets.bottom;
* size.height += buttonInsets.top + buttonInsets.bottom; size.height = Math.max(21,
* size.height); } else if (comboBox.isEditable() && arrowButton != null && editor != null) {
* size = super.getMinimumSize(c); Insets margin = arrowButton.getMargin(); Insets insets =
* comboBox.getInsets(); if (editor instanceof JComponent) { Insets editorInsets = ((JComponent)
* editor).getInsets(); } size.height += margin.top + margin.bottom; size.height += insets.top +
* insets.bottom;
*
* // size.height = Math.max(20,size.height); } else { size = super.getMinimumSize(c); }
*
* cachedMinimumSize.setSize(size.width, size.height); isMinimumSizeDirty = false;
*
* return new Dimension(cachedMinimumSize); }
*/
}
/trunk/OpenConcerto/src/org/openconcerto/task/config/TaskNX.java
17,6 → 17,7
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.ui.ConnexionPanel;
import org.openconcerto.sql.utils.Exiter;
import org.openconcerto.sql.view.EditPanel;
import org.openconcerto.task.ModelStateListener;
import org.openconcerto.task.TodoListPanel;
import org.openconcerto.ui.FrameUtil;
115,8 → 116,8
 
public void run() {
Toolkit.getDefaultToolkit().setDynamicLayout(true);
System.setProperty("org.openconcerto.editpanel.noborder", "true");
System.setProperty("org.openconcerto.editpanel.separator", "true");
System.setProperty(EditPanel.NOBORDER, "true");
System.setProperty(EditPanel.ADD_SEPARATOR, "true");
System.setProperty("org.openconcerto.listpanel.simpleui", "true");
 
try {
/trunk/OpenConcerto/src/org/openconcerto/task/config/ComptaBasePropsConfiguration.java
23,6 → 23,7
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.users.UserCommonSQLElement;
import org.openconcerto.sql.users.rights.RightSQLElement;
import org.openconcerto.sql.users.rights.UserRightSQLElement;
103,9 → 104,12
 
this.setProductInfo(productInfo);
String name = "ilm";
// don't overwrite (allow to map no roots, just to test connection)
if (getProperty("systemRoot.rootsToMap") == null) {
this.setProperty("systemRoot.rootsToMap", name + "_Common");
this.setProperty("systemRoot.rootPath", name + "_Common");
}
}
 
 
@Override
166,6 → 170,7
dir.addSQLElement(new TaskSQLElement());
 
dir.addSQLElement(new UserCommonSQLElement());
dir.addSQLElement(new CompanyAccessSQLElement());
dir.addSQLElement(UserRightSQLElement.class);
dir.addSQLElement(RightSQLElement.class);
 
/trunk/OpenConcerto/src/org/openconcerto/task/ui/UserRightsPrefPanel.java
31,6 → 31,7
import java.io.File;
 
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
67,12 → 68,12
});
l = new JList(dataModel);
l.setCellRenderer(new UserListCellRenderer());
l.setBorder(null);
l.setBorder(BorderFactory.createEmptyBorder());
final JScrollPane scrollPane = new JScrollPane(l);
scrollPane.setMinimumSize(new Dimension(120, 120));
scrollPane.setPreferredSize(new Dimension(120, 120));
this.add(scrollPane, c);
scrollPane.setBorder(null);
scrollPane.setBorder(BorderFactory.createEmptyBorder());
// Separator
c.gridx++;
JSeparator sep = new JSeparator(JSeparator.VERTICAL);
/trunk/OpenConcerto/src/org/openconcerto/task/ui/UserRightPanelDetail.java
153,7 → 153,7
c.gridx = 0;
c.gridwidth = 4;
JLabel labelHelp = new JLabel("Une autorisation désactivée apparait en gris clair");
labelHelp.setIcon(new ImageIcon(this.getClass().getResource("toc_open.gif")));
labelHelp.setIcon(new ImageIcon(UserRightPanelDetail.class.getResource("toc_open.gif")));
this.add(labelHelp, c);
 
this.addHierarchyListener(new DisplayabilityListener() {
/trunk/OpenConcerto/src/org/openconcerto/task/TodoListPanel.java
50,6 → 50,8
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.net.URL;
63,6 → 65,7
 
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.Icon;
import javax.swing.ImageIcon;
121,8 → 124,8
 
public TodoListPanel() {
this.setOpaque(false);
this.iconTache = new ImageIcon(this.getClass().getResource("tache.png"));
this.iconPriorite = new ImageIcon(this.getClass().getResource("priorite.png"));
this.iconTache = new ImageIcon(TodoListPanel.class.getResource("tache.png"));
this.iconPriorite = new ImageIcon(TodoListPanel.class.getResource("priorite.png"));
this.userTableCellRenderer = new UserTableCellRenderer();
this.timestampTableCellRendererCreated = new TimestampTableCellRenderer();
this.timestampTableCellRendererDone = new TimestampTableCellRenderer();
132,10 → 135,10
this.timestampTableCellEditorDeadLine = new TimestampTableCellEditor();
// Icon renderer
List<URL> l = new Vector<URL>();
l.add(this.getClass().getResource("empty.png"));
l.add(this.getClass().getResource("high.png"));
l.add(this.getClass().getResource("normal.png"));
l.add(this.getClass().getResource("low.png"));
l.add(TodoListPanel.class.getResource("empty.png"));
l.add(TodoListPanel.class.getResource("high.png"));
l.add(TodoListPanel.class.getResource("normal.png"));
l.add(TodoListPanel.class.getResource("low.png"));
this.iconEditor = new IconTableCellRenderer(l);
this.iconRenderer = new IconTableCellRenderer(l);
 
356,15 → 359,17
addTask();
}
});
this.detailCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
this.detailCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
// masque les colonnes "fait le" et "le"
detailCheckBoxClicked();
}
});
this.hideOldCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
TodoListPanel.this.model.setHistoryVisible(!TodoListPanel.this.hideOldCheckBox.isSelected());
this.hideOldCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
TodoListPanel.this.model.setHistoryVisible(e.getStateChange() == ItemEvent.DESELECTED);
TodoListPanel.this.model.asynchronousFill();
}
});
445,7 → 450,7
}
}
};
textField.setBorder(null);
textField.setBorder(BorderFactory.createEmptyBorder());
final DefaultCellEditor defaultCellEditor = new DefaultCellEditor(textField);
textField.addMouseListener(new MouseListener() {
 
705,7 → 710,7
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) this.renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
label.setIcon(this.icon);
label.setBorder(null);
label.setBorder(BorderFactory.createEmptyBorder());
label.setIconTextGap(0);
label.setHorizontalTextPosition(0);
label.setHorizontalAlignment(SwingConstants.CENTER);
/trunk/OpenConcerto/src/org/openconcerto/erp/storage/CloudStorageEngine.java
53,7 → 53,11
client.setVerifyHost(false);
StreamUtils.copy(inStream, localFile);
Log.get().info("Sending file:" + localFile.getCanonicalPath() + " to " + remotePath + " " + title + " size:" + localFile.length());
if (localFile.length() > 0) {
client.sendFile(localFile, remotePath, title, config.getToken());
} else {
Log.get().warning("Skiping empty file:" + localFile.getCanonicalPath() + " to " + remotePath + " " + title);
}
} catch (Exception e) {
throw new IOException(e);
} finally {
/trunk/OpenConcerto/src/org/openconcerto/erp/config/ComptaPropsConfiguration.java
55,6 → 55,7
import org.openconcerto.erp.core.finance.payment.element.EncaisserMontantElementSQLElement;
import org.openconcerto.erp.core.finance.payment.element.EncaisserMontantSQLElement;
import org.openconcerto.erp.core.finance.payment.element.ModeDeReglementSQLElement;
import org.openconcerto.erp.core.finance.payment.element.ReglerMontantElementSQLElement;
import org.openconcerto.erp.core.finance.payment.element.ReglerMontantSQLElement;
import org.openconcerto.erp.core.finance.payment.element.TypeReglementSQLElement;
import org.openconcerto.erp.core.finance.tax.element.EcoTaxeSQLElement;
129,6 → 130,8
import org.openconcerto.erp.core.supplychain.credit.element.AvoirFournisseurSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.CommandeElementSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.CommandeSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.FactureFournisseurElementSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.FactureFournisseurSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.SaisieAchatSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.TransferPurchaseSQLElement;
import org.openconcerto.erp.core.supplychain.order.element.TransferSupplierOrderSQLElement;
147,10 → 150,16
import org.openconcerto.erp.generationDoc.provider.AdresseVilleCPClientValueProvider;
import org.openconcerto.erp.generationDoc.provider.AdresseVilleClientValueProvider;
import org.openconcerto.erp.generationDoc.provider.AdresseVilleNomClientValueProvider;
import org.openconcerto.erp.generationDoc.provider.FacturableValueProvider;
import org.openconcerto.erp.generationDoc.provider.FormatedGlobalQtyTotalProvider;
import org.openconcerto.erp.generationDoc.provider.LabelAccountInvoiceProvider;
import org.openconcerto.erp.generationDoc.provider.MergedGlobalQtyTotalProvider;
import org.openconcerto.erp.generationDoc.provider.ModeDeReglementDetailsProvider;
import org.openconcerto.erp.generationDoc.provider.PrixUnitaireRemiseProvider;
import org.openconcerto.erp.generationDoc.provider.QteTotalProvider;
import org.openconcerto.erp.generationDoc.provider.RefClientValueProvider;
import org.openconcerto.erp.generationDoc.provider.TotalAcompteProvider;
import org.openconcerto.erp.generationDoc.provider.TotalCommandeClientProvider;
import org.openconcerto.erp.generationDoc.provider.UserCreateInitialsValueProvider;
import org.openconcerto.erp.generationDoc.provider.UserCurrentInitialsValueProvider;
import org.openconcerto.erp.generationDoc.provider.UserModifyInitialsValueProvider;
172,6 → 181,7
import org.openconcerto.erp.injector.DevisEltFactureEltSQLInjector;
import org.openconcerto.erp.injector.DevisFactureSQLInjector;
import org.openconcerto.erp.injector.EcheanceEncaisseSQLInjector;
import org.openconcerto.erp.injector.EcheanceRegleSQLInjector;
import org.openconcerto.erp.injector.FactureAvoirSQLInjector;
import org.openconcerto.erp.injector.FactureBonSQLInjector;
import org.openconcerto.erp.injector.FactureCommandeSQLInjector;
191,6 → 201,7
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLServer;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.task.TacheActionManager;
import org.openconcerto.task.config.ComptaBasePropsConfiguration;
244,6 → 255,13
public static final ProductInfo productInfo = ProductInfo.getInstance();
public static final String APP_NAME = productInfo.getName();
private static final String DEFAULT_ROOT = "Common";
 
static final Properties createDefaults() {
final Properties defaults = new Properties();
defaults.setProperty("base.root", DEFAULT_ROOT);
return defaults;
}
 
// the properties path from this class
private static final String PROPERTIES = "main.properties";
 
301,8 → 319,7
// Log pour debug demarrage
System.out.println("Loading configuration from:" + (confFile == null ? "null" : confFile.getAbsolutePath()));
final boolean inWebStart = Gestion.inWebStart();
final Properties defaults = new Properties();
defaults.setProperty("base.root", DEFAULT_ROOT);
final Properties defaults = createDefaults();
// Ordre de recherche:
// a/ fichier de configuration
// b/ dans le jar
321,7 → 338,7
else
throw new IOException("found neither " + confFile + " nor embedded " + PROPERTIES);
}
return new ComptaPropsConfiguration(props, inWebStart);
return new ComptaPropsConfiguration(props, inWebStart, true);
} catch (final IOException e) {
e.printStackTrace();
ExceptionHandler.die("Impossible de lire le fichier de configuration.", e);
333,13 → 350,15
 
// *** instance
 
private final boolean isMain;
private final boolean inWebstart;
private final boolean isServerless;
private boolean isOnCloud;
private FieldMapper fieldMapper;
 
ComptaPropsConfiguration(Properties props, final boolean inWebstart) {
// isMain=true also set up some VM wide settings
ComptaPropsConfiguration(Properties props, final boolean inWebstart, final boolean main) {
super(props, productInfo);
this.isMain = main;
this.inWebstart = inWebstart;
this.setProperty("wd", DesktopEnvironment.getDE().getDocumentsFolder().getAbsolutePath() + File.separator + this.getAppName());
if (this.getProperty("version.date") != null) {
396,12 → 415,11
// Local database
setProperty("server.login", "openconcerto");
setProperty("server.password", "openconcerto");
this.isServerless = getProperty("server.ip", "").contains(DATA_DIR_VAR);
if (this.isServerless) {
this.setProperty("server.ip", getProperty("server.ip").replace(DATA_DIR_VAR, getDataDir().getPath()));
final SQLSystem system = getSystem();
this.isServerless = system == SQLSystem.H2 && system.getHostname(getServerIp()) == null;
}
}
 
if (this.isMain) {
// ATTN this works because this is executed last (i.e. if you put this in a superclass
// this won't work since e.g. app.name won't have its correct value)
this.setupLogging("logs");
408,6 → 426,7
registerAccountingProvider();
registerCellValueProvider();
}
}
 
private void registerAccountingProvider() {
SalesInvoiceAccountingRecordsProvider.register();
420,6 → 439,10
UserModifyInitialsValueProvider.register();
UserCurrentInitialsValueProvider.register();
PrixUnitaireRemiseProvider.register();
TotalAcompteProvider.register();
FacturableValueProvider.register();
TotalCommandeClientProvider.register();
LabelAccountInvoiceProvider.register();
AdresseRueClientValueProvider.register();
AdresseVilleClientValueProvider.register();
AdresseVilleCPClientValueProvider.register();
428,6 → 451,8
QteTotalProvider.register();
RefClientValueProvider.register();
ModeDeReglementDetailsProvider.register();
FormatedGlobalQtyTotalProvider.register();
MergedGlobalQtyTotalProvider.register();
}
 
@Override
495,7 → 520,9
@Override
public void destroy() {
// since we used setupLogging() in the constructor (allows to remove confDir)
if (this.isMain) {
this.tearDownLogging(true);
}
super.destroy();
}
 
751,9 → 778,12
dir.addSQLElement(new RelanceSQLElement());
dir.addSQLElement(new ReglementPayeSQLElement());
dir.addSQLElement(new ReglerMontantSQLElement());
dir.addSQLElement(ReglerMontantElementSQLElement.class);
dir.addSQLElement(RepartitionAnalytiqueSQLElement.class);
 
dir.addSQLElement(new SaisieAchatSQLElement());
dir.addSQLElement(new FactureFournisseurSQLElement());
dir.addSQLElement(new FactureFournisseurElementSQLElement());
dir.addSQLElement(new TransferPurchaseSQLElement());
dir.addSQLElement(new SaisieKmSQLElement());
dir.addSQLElement(new SaisieVenteComptoirSQLElement());
819,6 → 849,7
new CommandeBrSQLInjector(rootSociete);
new CommandeFactureAchatSQLInjector(rootSociete);
new EcheanceEncaisseSQLInjector(rootSociete);
new EcheanceRegleSQLInjector(rootSociete);
new BrFactureAchatSQLInjector(rootSociete);
new DevisEltFactureEltSQLInjector(rootSociete);
}
831,6 → 862,7
showAs.show("ACTIVITE", "CODE_ACTIVITE");
showAs.show("ADRESSE", SQLRow.toList("RUE,CODE_POSTAL,VILLE"));
final DBRoot root = this.getRootSociete();
 
showAs.show("AXE_ANALYTIQUE", "NOM");
 
showAs.show("CHEQUE_A_ENCAISSER", "MONTANT", "ID_CLIENT");
882,7 → 914,6
showAs.show("MODELE_COURRIER_CLIENT", "NOM", "CONTENU");
 
showAs.show("NATURE_COMPTE", "NOM");
 
showAs.show("POSTE_ANALYTIQUE", "NOM");
showAs.show("PAYS", "CODE", "NOM");
showAs.show("PIECE", "ID", "NOM");
920,15 → 951,20
 
}
 
public void setUpSocieteDataBaseConnexion(int base) {
public String setUpSocieteStructure(int base) {
setRowSociete(base);
 
// find customer
String customerName = "openconcerto";
final DBRoot rootSociete = this.getRootSociete();
final String dbMD = rootSociete.getMetadata("CUSTOMER");
final String dbMD = getRootSociete().getMetadata("CUSTOMER");
if (dbMD != null && !dbMD.equals(customerName))
throw new IllegalStateException("customer is '" + customerName + "' but db says '" + dbMD + "'");
return customerName;
}
 
public void setUpSocieteDataBaseConnexion(int base) {
final String customerName = setUpSocieteStructure(base);
final DBRoot rootSociete = this.getRootSociete();
closeSocieteConnexion();
setSocieteDirectory();
NumerotationAutoSQLElement.addListeners();
942,14 → 978,11
}
 
private void setMapper() {
fieldMapper = new FieldMapper(this.getRootSociete());
FieldMapper fieldMapper = new FieldMapper(this.getRootSociete());
fieldMapper.addMapperStreamFromClass(Gestion.class);
setFieldMapper(fieldMapper);
}
 
public FieldMapper getFieldMapper() {
return fieldMapper;
}
 
private void closeSocieteConnexion() {
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/config/MenuManager.java
22,17 → 22,25
import javax.swing.Action;
 
public class MenuManager {
private static final MenuManager instance = new MenuManager();
private static MenuManager instance = null;
 
public static final MenuManager getInstance() {
public static synchronized final void setInstance(final MenuAndActions baseMA) {
instance = new MenuManager(baseMA);
}
 
public static synchronized final MenuManager getInstance() {
if (instance == null)
throw new IllegalStateException("Not inited");
return instance;
}
 
private MenuAndActions baseMA;
private MenuAndActions menuAndActions;
private Group group;
private final PropertyChangeSupport supp = new PropertyChangeSupport(this);
 
{
public MenuManager(final MenuAndActions baseMA) {
this.baseMA = baseMA;
this.setMenuAndActions(this.createBaseMenuAndActions());
assert this.group != null;
}
56,7 → 64,7
}
 
public final MenuAndActions createBaseMenuAndActions() {
return (Gestion.isMinimalMode() ? new MinimalMenuConfiguration() : new DefaultMenuConfiguration()).createMenuAndActions();
return this.baseMA.copy();
}
 
public final MenuAndActions copyMenuAndActions() {
/trunk/OpenConcerto/src/org/openconcerto/erp/config/MainFrame.java
54,7 → 54,10
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
 
import net.jcip.annotations.GuardedBy;
 
public class MainFrame extends JFrame {
 
// menus
70,13 → 73,26
public static final String HELP_MENU = "menu.help";
 
static private final List<Runnable> runnables = new ArrayList<Runnable>();
@GuardedBy("MainFrame")
static private MainFrame instance = null;
 
public static MainFrame getInstance() {
// thread safe
public synchronized static MainFrame getInstance() {
return instance;
}
 
private static void setInstance(MainFrame f) {
public synchronized static MainFrame resetInstance() {
final MainFrame res = instance;
setInstance(null);
if (res != null) {
res.setVisible(false);
res.dispose();
}
return res;
}
 
private synchronized static void setInstance(MainFrame f) {
assert SwingUtilities.isEventDispatchThread();
if (f != null && instance != null)
throw new IllegalStateException("More than one main frame");
instance = f;
98,7 → 114,7
SwingThreadUtils.invoke(new Runnable() {
@Override
public void run() {
if (instance == null) {
if (getInstance() == null) {
runnables.add(r);
} else {
r.run();
118,7 → 134,7
public MainFrame() {
super();
 
this.setIconImage(new ImageIcon(this.getClass().getResource("frameicon.png")).getImage());
this.setIconImage(new ImageIcon(MainFrame.class.getResource("frameicon.png")).getImage());
 
Container co = this.getContentPane();
co.setLayout(new GridBagLayout());
163,7 → 179,8
}
this.setMinimumSize(minSize);
 
final File confFile = new File(Configuration.getInstance().getConfDir(), "Configuration" + File.separator + "Frame" + File.separator + "mainFrame" + confSuffix + ".xml");
final Configuration conf = Configuration.getInstance();
final File confFile = new File(conf.getConfDir(), "Configuration" + File.separator + "Frame" + File.separator + "mainFrame" + confSuffix + ".xml");
new WindowStateManager(this, confFile).loadState();
 
registerForMacOSXEvents();
177,7 → 194,7
 
setInstance(this);
// Overrive logo
Image im = ComptaPropsConfiguration.getInstanceCompta().getCustomLogo();
final Image im = conf instanceof ComptaPropsConfiguration ? ((ComptaPropsConfiguration) conf).getCustomLogo() : null;
if (im != null) {
image.setImage(im);
}
/trunk/OpenConcerto/src/org/openconcerto/erp/config/DefaultMenuConfiguration.java
37,6 → 37,7
import org.openconcerto.erp.core.finance.accounting.action.GestionPlanComptableEAction;
import org.openconcerto.erp.core.finance.accounting.action.ImpressionLivrePayeAction;
import org.openconcerto.erp.core.finance.accounting.action.ListeDesEcrituresAction;
import org.openconcerto.erp.core.finance.accounting.action.ListeDesJournauxAction;
import org.openconcerto.erp.core.finance.accounting.action.ListeEcritureParClasseAction;
import org.openconcerto.erp.core.finance.accounting.action.NouveauClotureAction;
import org.openconcerto.erp.core.finance.accounting.action.NouveauJournalAction;
86,7 → 87,6
import org.openconcerto.erp.core.sales.order.action.ListeDesCommandesClientAction;
import org.openconcerto.erp.core.sales.order.action.NouvelleCommandeClientAction;
import org.openconcerto.erp.core.sales.pos.action.ListeDesCaissesTicketAction;
import org.openconcerto.erp.core.sales.pos.action.ListeDesTicketsAction;
import org.openconcerto.erp.core.sales.product.action.FamilleArticleAction;
import org.openconcerto.erp.core.sales.product.action.ListeDesArticlesAction;
import org.openconcerto.erp.core.sales.quote.action.ListeDesDevisAction;
98,9 → 98,11
import org.openconcerto.erp.core.supplychain.credit.action.ListeDesAvoirsFournisseurAction;
import org.openconcerto.erp.core.supplychain.credit.action.NouvelAvoirFournisseurAction;
import org.openconcerto.erp.core.supplychain.order.action.ListeDesCommandesAction;
import org.openconcerto.erp.core.supplychain.order.action.ListeDesFacturesFournisseurAction;
import org.openconcerto.erp.core.supplychain.order.action.ListeSaisieAchatAction;
import org.openconcerto.erp.core.supplychain.order.action.NouveauSaisieAchatAction;
import org.openconcerto.erp.core.supplychain.order.action.NouvelleCommandeAction;
import org.openconcerto.erp.core.supplychain.order.action.NouvelleFactureFournisseurAction;
import org.openconcerto.erp.core.supplychain.receipt.action.ListeDesBonsReceptionsAction;
import org.openconcerto.erp.core.supplychain.receipt.action.NouveauBonReceptionAction;
import org.openconcerto.erp.core.supplychain.stock.action.ListeDesMouvementsStockAction;
113,16 → 115,22
import org.openconcerto.erp.preferences.DefaultNXProps;
import org.openconcerto.erp.rights.ComptaUserRight;
import org.openconcerto.erp.rights.NXRights;
import org.openconcerto.erp.utils.correct.CorrectMouvement;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.users.rights.LockAdminUserRight;
import org.openconcerto.sql.users.rights.UserRights;
import org.openconcerto.ui.FrameUtil;
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.group.LayoutHints;
 
import java.awt.Dimension;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.List;
 
import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
 
public class DefaultMenuConfiguration implements MenuConfiguration {
public void registerMenuTranslations() {
226,6 → 234,7
supplierGroup.addItem("supplier.order.create");
supplierGroup.addItem("supplier.receipt.create");
supplierGroup.addItem("supplier.purchase.create");
supplierGroup.addItem("supplier.invoice.purchase.create");
supplierGroup.addItem("supplier.credit.create");
group.addItem("stock.io.create");
}
249,6 → 258,7
final Group gAccounting = new Group("menu.organization.accounting", LayoutHints.DEFAULT_NOLABEL_SEPARATED_GROUP_HINTS);
gAccounting.addItem("accounting.chart");
gAccounting.addItem("accounting.journal");
gAccounting.addItem("accounting.checkDB");
group.add(gAccounting);
}
 
339,7 → 349,7
 
private Group createStatsDocumentsGroup() {
final Group group = new Group(MainFrame.DECLARATION_MENU);
group.addItem("accounting.vat.report");
// group.addItem("accounting.vat.report");
group.addItem("accounting.costs.report");
group.addItem("accounting.balance.report");
group.addItem("employe.social.report");
403,6 → 413,7
gSupplier.addItem("supplier.order.list");
gSupplier.addItem("supplier.receipt.list");
gSupplier.addItem("supplier.purchase.list");
gSupplier.addItem("supplier.invoice.purchase.list");
gSupplier.addItem("supplier.credit.list");
}
group.add(gSupplier);
412,9 → 423,6
gProduct.addItem("stock.io.list");
group.add(gProduct);
 
final Group gPos = new Group("menu.list.pos", LayoutHints.DEFAULT_NOLABEL_SEPARATED_GROUP_HINTS);
gPos.addItem("pos.receipt.list");
group.add(gPos);
return group;
}
 
432,10 → 440,7
mManager.registerAction("modules", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
final ModuleFrame frame = new ModuleFrame();
frame.setMinimumSize(new Dimension(480, 640));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
FrameUtil.show(ModuleFrame.getInstance());
}
});
if (!Gestion.MAC_OS_X) {
475,6 → 480,7
mManager.registerAction("supplier.order.create", new NouvelleCommandeAction());
mManager.registerAction("supplier.receipt.create", new NouveauBonReceptionAction());
mManager.registerAction("supplier.purchase.create", new NouveauSaisieAchatAction());
mManager.registerAction("supplier.invoice.purchase.create", new NouvelleFactureFournisseurAction());
mManager.registerAction("supplier.credit.create", new NouvelAvoirFournisseurAction());
mManager.registerAction("stock.io.create", new NouvelleSaisieMouvementStockAction());
}
518,6 → 524,7
mManager.registerAction("supplier.order.list", new ListeDesCommandesAction());
mManager.registerAction("supplier.receipt.list", new ListeDesBonsReceptionsAction());
mManager.registerAction("supplier.purchase.list", new ListeSaisieAchatAction());
mManager.registerAction("supplier.invoice.purchase.list", new ListeDesFacturesFournisseurAction());
mManager.registerAction("supplier.credit.list", new ListeDesAvoirsFournisseurAction());
}
 
524,7 → 531,6
mManager.registerAction("product.list", new ListeDesArticlesAction());
mManager.registerAction("stock.io.list", new ListeDesMouvementsStockAction());
 
mManager.registerAction("pos.receipt.list", new ListeDesTicketsAction());
 
}
 
540,7 → 546,7
}
 
private void registerStatsDocumentsActions(final MenuAndActions mManager) {
mManager.registerAction("accounting.vat.report", new DeclarationTVAAction());
// mManager.registerAction("accounting.vat.report", new DeclarationTVAAction());
mManager.registerAction("accounting.costs.report", new EtatChargeAction());
mManager.registerAction("accounting.balance.report", new CompteResultatBilanAction());
mManager.registerAction("employe.social.report", new N4DSAction());
605,8 → 611,18
final ComptaPropsConfiguration configuration = ComptaPropsConfiguration.getInstanceCompta();
if (rights.haveRight(ComptaUserRight.MENU)) {
mManager.registerAction("accounting.chart", new GestionPlanComptableEAction());
mManager.registerAction("accounting.journal", new NouveauJournalAction());
mManager.registerAction("accounting.journal", new ListeDesJournauxAction());
mManager.putAction(new AbstractAction("Check DB") {
@Override
public void actionPerformed(ActionEvent e) {
final DBRoot rootSociete = ComptaPropsConfiguration.getInstanceCompta().getRootSociete();
final SQLSelect sel = CorrectMouvement.createUnbalancedSelect(rootSociete);
final List<?> ids = rootSociete.getDBSystemRoot().getDataSource().executeCol(sel.asString());
JOptionPane.showMessageDialog((Component) e.getSource(), "Il y a " + ids.size() + " mouvement(s) non équilibré(s).", "Résultat", ids.size() == 0 ? JOptionPane.INFORMATION_MESSAGE
: JOptionPane.WARNING_MESSAGE);
}
}, "accounting.checkDB");
}
 
if (rights.haveRight(LockAdminUserRight.LOCK_MENU_ADMIN)) {
mManager.registerAction("user.list", new ListeDesUsersCommonAction());
/trunk/OpenConcerto/src/org/openconcerto/erp/config/SQLElementNames_fr.xml
New file
0,0 → 1,5
<translations>
<element refid="CONTACT" nameClass="masculine" name="contact" />
<element refid="CONTACT_FOURNISSEUR" nameClass="masculine" name="contact fournisseur" namePlural="contacts fournisseurs" />
<element refid="CONTACT_ADMINISTRATIF" nameClass="masculine" name="contact administratif" namePlural="contacts administratifs" />
</translations>
/trunk/OpenConcerto/src/org/openconcerto/erp/config/translation_fr.xml
2,7 → 2,7
<!-- File -->
<menu id="menu.file" label="Fichier" />
<menu id="backup" label="Sauvegarde" />
<menu id="export.accounting" label="Export relation expert" />
<menu id="export.accounting" label="Export des écritures" />
<menu id="modules" label="Modules" />
<menu id="preferences" label="Préférences" />
<menu id="quit" label="Quitter" />
73,7 → 73,7
<!-- Accounting -->
<menu id="menu.accounting" label="Etats" />
<menu id="accounting.balance" label="Balance" />
<menu id="accounting.client.balance" label="Balance agée" />
<menu id="accounting.client.balance" label="Balance âgée" />
<menu id="accounting.analytical.result" label="Compte de résultat analytique" />
<menu id="accounting.ledger" label="Journaux" />
<menu id="accounting.general.ledger" label="Grand livre" />
129,6 → 129,7
<menu id="menu.organization" label="Structure" />
<menu id="accounting.chart" label="Plan comptable de l'entreprise" />
<menu id="accounting.journal" label="Gestion des journaux comptables" />
<menu id="accounting.checkDB" label="Vérifier la structure de la base" />
<menu id="user.list" label="Liste des utilisateurs" />
<menu id="user.right.list" label="Gestion des droits" />
<menu id="user.task.right" label="Autorisations liées aux tâches" />
/trunk/OpenConcerto/src/org/openconcerto/erp/config/mappingCompta_fr.xml
26,49 → 26,34
<TABLE name="AFFAIRE">
<FIELD name="ID_CLIENT" label="Client" titlelabel="Client" />
<FIELD name="CCI" label="CCI" titlelabel="CCI" />
<FIELD name="ID_VERIFICATEUR" label="Chargé d'affaire"
titlelabel="Chargé d'affaire" />
<FIELD name="DATE_DEMARRAGE" label="Date de démarrage"
titlelabel="Date de démarrage" />
<FIELD name="ID_VERIFICATEUR" label="Chargé d'affaire" titlelabel="Chargé d'affaire" />
<FIELD name="DATE_DEMARRAGE" label="Date de démarrage" titlelabel="Date de démarrage" />
<FIELD name="MARCHE" label="Marché" titlelabel="Marché" />
<FIELD name="NUMERO" label="Numéro affaire" titlelabel="Numéro affaire" />
<FIELD name="DATE" label="Date" titlelabel="Date" />
<FIELD name="ID_CONTACT_COM" label="Contact commercial"
titlelabel="Contact commercial" />
<FIELD name="ID_CONTACT_TECH" label="Contact technique"
titlelabel="Contact technique" />
<FIELD name="ID_CONTACT_COM" label="Contact commercial" titlelabel="Contact commercial" />
<FIELD name="ID_CONTACT_TECH" label="Contact technique" titlelabel="Contact technique" />
<FIELD name="ID_PROPOSITION" label="Proposition" titlelabel="Proposition" />
<FIELD name="OBJET" label="Affaire" titlelabel="Affaire" />
<FIELD name="TOTAL_HT" label="Total HT" titlelabel="Total HT" />
<FIELD name="MONTANT_FACTURE" label="Montant HT facturé"
titlelabel="Montant HT facturé" />
<FIELD name="MONTANT_FACTURE" label="Montant HT facturé" titlelabel="Montant HT facturé" />
<FIELD name="REFERENCE" label="Nos références" titlelabel="Nos références" />
<FIELD name="SITUATION_ADMIN" label="Situation administrative"
titlelabel="Situation administrative" />
<FIELD name="REFERENCE_CLIENT" label="Références client"
titlelabel="Références client" />
<FIELD name="SITUATION_ADMIN" label="Situation administrative" titlelabel="Situation administrative" />
<FIELD name="REFERENCE_CLIENT" label="Références client" titlelabel="Références client" />
<FIELD name="ACTIVITE" label="Activité" titlelabel="Activité" />
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="INFOS_ETIQUETTE" label="Informations étiquette"
titlelabel="Informations étiquette" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="INFOS_ETIQUETTE" label="Informations étiquette" titlelabel="Informations étiquette" />
<FIELD name="MONTANT_REVISABLE" label="Révisable" titlelabel="Révisable" />
<FIELD name="ID_ADRESSE" label="Adresse de facturation"
titlelabel="Adresse de facturation" />
<FIELD name="ID_ADRESSE_COURRIER_1" label="Adresse de courrier 1"
titlelabel="Adresse de courrier 1" />
<FIELD name="ID_ADRESSE_COURRIER_2" label="Adresse de courrier 2"
titlelabel="Adresse de courrier 2" />
<FIELD name="ID_ADRESSE_COURRIER_3" label="Adresse de courrier 3"
titlelabel="Adresse de courrier 3" />
<FIELD name="ID_ADRESSE" label="Adresse de facturation" titlelabel="Adresse de facturation" />
<FIELD name="ID_ADRESSE_COURRIER_1" label="Adresse de courrier 1" titlelabel="Adresse de courrier 1" />
<FIELD name="ID_ADRESSE_COURRIER_2" label="Adresse de courrier 2" titlelabel="Adresse de courrier 2" />
<FIELD name="ID_ADRESSE_COURRIER_3" label="Adresse de courrier 3" titlelabel="Adresse de courrier 3" />
</TABLE>
<TABLE name="AFFAIRE_ELEMENT">
<FIELD name="FIN_CONTRAT" label="Fin de contrat" titlelabel="Fin de contrat" />
<FIELD name="DATE_FIN_CONTRAT" label="Date de fin de contrat"
titlelabel="Date de fin de contrat" />
<FIELD name="DATE_FIN_CONTRAT" label="Date de fin de contrat" titlelabel="Date de fin de contrat" />
<FIELD name="CODE" label="Code" titlelabel="Code" />
<FIELD name="DUREE_PREVISIONNELLE" label="Durée prévue"
titlelabel="Durée prévue" />
<FIELD name="DUREE_PREVISIONNELLE" label="Durée prévue" titlelabel="Durée prévue" />
<FIELD name="ID_DOSSIER" label="Dossier" titlelabel="Dossier" />
<FIELD name="ID_TAXE" label="TVA" titlelabel="TVA" />
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
83,41 → 68,29
<FIELD name="OBJET_INSPECTE" label="Objet inspecté" titlelabel="Objet inspecté" />
<FIELD name="PV_UNIT_HT" label="PV unitaire HT" titlelabel="PV unitaire HT" />
<FIELD name="MONTANT_HT" label="Montant HT" titlelabel="Montant HT" />
<FIELD name="REFERENTIEL_INSPECTION" label="Référentiel inspection"
titlelabel="Référentiel inspection" />
<FIELD name="LOCAL_OBJET_INSPECTE" label="Localisation objet"
titlelabel="Localisation objet" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité"
titlelabel="Secteur d'activité" />
<FIELD name="ID_NATURE_MISSION" label="Nature mission"
titlelabel="Nature mission" />
<FIELD name="REFERENTIEL_INSPECTION" label="Référentiel inspection" titlelabel="Référentiel inspection" />
<FIELD name="LOCAL_OBJET_INSPECTE" label="Localisation objet" titlelabel="Localisation objet" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité" titlelabel="Secteur d'activité" />
<FIELD name="ID_NATURE_MISSION" label="Nature mission" titlelabel="Nature mission" />
<FIELD name="ID_DOMAINE" label="Domaine" titlelabel="Domaine" />
<FIELD name="REPARTITION_POURCENT" label="Répartition %"
titlelabel="Répartition %" />
<FIELD name="REPARTITION_POURCENT" label="Répartition %" titlelabel="Répartition %" />
<FIELD name="MONTANT_REVISABLE" label="Révisable" titlelabel="Révisable" />
<FIELD name="INFOS_OBJET_INSPECTE" label="Commentaires objet"
titlelabel="Commentaires objet" />
<FIELD name="ID_SITE_INTERVENTION" label="Site d'intervention"
titlelabel="Site d'intervention" />
<FIELD name="SITUATION_ADMIN" label="Situation administrative"
titlelabel="Situation administrative" />
<FIELD name="INFOS_OBJET_INSPECTE" label="Commentaires objet" titlelabel="Commentaires objet" />
<FIELD name="ID_SITE_INTERVENTION" label="Site d'intervention" titlelabel="Site d'intervention" />
<FIELD name="SITUATION_ADMIN" label="Situation administrative" titlelabel="Situation administrative" />
<FIELD name="NOTA" label="Nota" titlelabel="Nota" />
</TABLE>
 
<TABLE name="ARTICLE">
<FIELD name="ID_UNITE_VENTE" label="Unité de vente" titlelabel="Unité de vente" />
<FIELD name="QTE_ACHAT" label="Quantité multiple d'achat à respecter"
titlelabel="Quantité multiple d'achat à respecter" />
<FIELD name="QTE_ACHAT" label="Quantité multiple d'achat à respecter" titlelabel="Quantité multiple d'achat à respecter" />
<FIELD name="CODE_BARRE" label="Code barres" titlelabel="Code barres" />
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="ID_FOURNISSEUR" label="Fournisseur principal"
titlelabel="Fournisseur principal" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="ID_FOURNISSEUR" label="Fournisseur principal" titlelabel="Fournisseur principal" />
<FIELD name="GESTION_STOCK" label="Gérer le stock" titlelabel="Gérer le stock" />
<FIELD name="CODE_DOUANIER" label="Code douanier" titlelabel="Code douanier" />
<FIELD name="ID_PAYS" label="Pays d'origine" titlelabel="Pays d'origine" />
<FIELD name="QTE_MIN" label="Quantité minimum à stocker"
titlelabel="Quantité minimum à stocker" />
<FIELD name="QTE_MIN" label="Quantité minimum à stocker" titlelabel="Quantité minimum à stocker" />
<FIELD name="ETIQUETTE" label="Libellé étiquette" titlelabel="Libellé étiquette" />
<FIELD name="CODE" label="Code article" titlelabel="Code article" />
<FIELD name="NOM" label="Désignation" titlelabel="Désignation" />
130,8 → 103,7
<FIELD name="ID_STOCK" label="Stock" titlelabel="Stock" />
<FIELD name="PRIX_METRIQUE_VT_1" label="P.V. UV HT" titlelabel="P.V. UV HT" />
<FIELD name="PRIX_METRIQUE_HA_1" label="P.A. UV HT" titlelabel="P.A. UV HT" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut"
titlelabel="Longueur par défaut" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut" titlelabel="Longueur par défaut" />
<FIELD name="ID_METRIQUE_1" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV HT" titlelabel="PV HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA HT" titlelabel="PHA HT" />
141,19 → 113,14
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA HT" titlelabel="PHA HT" />
<FIELD name="VALEUR_METRIQUE_3" label="poids / m²" titlelabel="poids / m²" />
<FIELD name="ID_METRIQUE_3" label="Métrique" titlelabel="Métrique" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente"
titlelabel="Mode de vente" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente" titlelabel="Mode de vente" />
<FIELD name="ID_FAMILLE_ARTICLE" label="Famille" titlelabel="Famille" />
<FIELD name="OBSOLETE" label="Obsolete" titlelabel="Obsolete" />
<FIELD name="PRIX_REVENTE_HT" label="Prix de revente ht"
titlelabel="Prix de revente ht" />
<FIELD name="PRIX_FINAL_TTC" label="Prix client final ttc"
titlelabel="Prix client final ttc" />
<FIELD name="PRIX_REVENTE_HT" label="Prix de revente ht" titlelabel="Prix de revente ht" />
<FIELD name="PRIX_FINAL_TTC" label="Prix client final ttc" titlelabel="Prix client final ttc" />
<FIELD name="ID_ECOTAXE" label="Ecotaxe" titlelabel="Ecotaxe" />
<FIELD name="ID_COMPTE_PCE" label="Compte spécifique de vente"
titlelabel="Compte spécifique de vente" />
<FIELD name="ID_COMPTE_PCE_ACHAT" label="Compte spécifique d'achat"
titlelabel="Compte spécifique d'achat" />
<FIELD name="ID_COMPTE_PCE" label="Compte spécifique de vente" titlelabel="Compte spécifique de vente" />
<FIELD name="ID_COMPTE_PCE_ACHAT" label="Compte spécifique d'achat" titlelabel="Compte spécifique d'achat" />
</TABLE>
 
<TABLE name="ARTICLE_TARIF">
160,10 → 127,8
<FIELD name="ID_TARIF" label="Tarif" titlelabel="Tarif" />
<FIELD name="ID_TAXE" label="Taxe" titlelabel="Taxe" />
<FIELD name="ID_DEVISE" label="Devise" titlelabel="Devise" />
<FIELD name="PRIX_REVENTE_HT" label="Prix de revente ht"
titlelabel="Prix de revente ht" />
<FIELD name="PRIX_FINAL_TTC" label="Prix client final ttc"
titlelabel="Prix client final ttc" />
<FIELD name="PRIX_REVENTE_HT" label="Prix de revente ht" titlelabel="Prix de revente ht" />
<FIELD name="PRIX_FINAL_TTC" label="Prix client final ttc" titlelabel="Prix client final ttc" />
<FIELD name="PV_HT" label="Prix vente HT" titlelabel="Prix vente HT" />
<FIELD name="PV_TTC" label="Prix vente TTC" titlelabel="Prix vente TTC" />
 
177,38 → 142,32
</TABLE>
 
<TABLE name="ASSOCIATION_ANALYTIQUE">
<FIELD name="ID_POSTE_ANALYTIQUE" label="Poste analytique"
titlelabel="Poste analytique" />
<FIELD name="ID_POSTE_ANALYTIQUE" label="Poste analytique" titlelabel="Poste analytique" />
</TABLE>
 
<TABLE name="ASSOCIATION_COMPTE_ANALYTIQUE">
<FIELD name="ID_COMPTE_PCE" label="Compte" titlelabel="Compte" />
<FIELD name="ID_AXE_ANALYTIQUE" label="Axe" titlelabel="Axe" />
<FIELD name="ID_REPARTITION_ANALYTIQUE" label="Répartition"
titlelabel="Répartition" />
<FIELD name="ID_REPARTITION_ANALYTIQUE" label="Répartition" titlelabel="Répartition" />
</TABLE>
 
<TABLE name="AVIS_INTERVENTION">
<FIELD name="ID_SECRETAIRE" label="Secretaire" titlelabel="Secretaire" />
<FIELD name="ID_FICHE_RENDEZ_VOUS" label="Fiche de rendez vous"
titlelabel="Fiche de rendez vous" />
<FIELD name="ID_FICHE_RENDEZ_VOUS" label="Fiche de rendez vous" titlelabel="Fiche de rendez vous" />
<FIELD name="DATE" label="Date" titlelabel="Date" />
</TABLE>
 
<TABLE name="AVOIR_CLIENT">
<FIELD name="ID_SECRETAIRE" label="Secrétaire" titlelabel="Secrétaire" />
<FIELD name="ID_ECHEANCIER_CCI" label="Echeancier CCI"
titlelabel="Echeancier CCI" />
<FIELD name="ID_ECHEANCIER_CCI" label="Echeancier CCI" titlelabel="Echeancier CCI" />
<FIELD name="REF_CLIENT" label="Références client" titlelabel="Références client" />
<FIELD name="ID_MODE_REGLEMENT" label="Mode de reglement"
titlelabel="Mode de reglement" />
<FIELD name="ID_MODE_REGLEMENT" label="Mode de reglement" titlelabel="Mode de reglement" />
<FIELD name="ID_ADRESSE" label="Adresse spécifique" titlelabel="Adresse spécifique" />
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
<FIELD name="ID_AFFAIRE" label="Affaire" titlelabel="Affaire" />
<FIELD name="AFFACTURE" label="Natexis" titlelabel="Natexis" />
<FIELD name="ID_CONTACT" label="Contact" titlelabel="Contact" />
<FIELD name="MONTANT_RESTANT" label="Montant TTC Restant"
titlelabel="Montant TTC Restant" />
<FIELD name="MONTANT_RESTANT" label="Montant TTC Restant" titlelabel="Montant TTC Restant" />
<FIELD name="DATE" label="Date" titlelabel="Date" />
<FIELD name="SOLDE" label="Soldé" titlelabel="Soldé" />
<FIELD name="MONTANT_HT" label="Montant HT" titlelabel="Montant HT" />
216,31 → 175,27
<FIELD name="ID_TAXE" label="Taxe" titlelabel="Taxe" />
<FIELD name="ID_CLIENT" label="Client" titlelabel="Client" />
<FIELD name="ID_POLE_PRODUIT" label="Pôle produit" titlelabel="Pôle produit" />
<FIELD name="ID_SAISIE_VENTE_FACTURE" label="Facture N°"
titlelabel="Facture N°" />
<FIELD name="ID_SAISIE_VENTE_FACTURE" label="Facture N°" titlelabel="Facture N°" />
<FIELD name="NUMERO" label="Numéro" titlelabel="Numéro" />
<FIELD name="MONTANT_TVA" label="Total TVA" titlelabel="Total TVA" />
<FIELD name="PORT_HT" label="Frais de port HT" titlelabel="Ports" />
<FIELD name="REMISE_HT" label="Remise HT" titlelabel="Remise" />
<FIELD name="MONTANT_SOLDE" label="Montant TTC soldé"
titlelabel="Montant TTC soldé" />
<FIELD name="MONTANT_SERVICE" label="Frais de service"
titlelabel="Service" />
<FIELD name="MONTANT_SOLDE" label="Montant TTC soldé" titlelabel="Montant TTC soldé" />
<FIELD name="MONTANT_SERVICE" label="Frais de service" titlelabel="Service" />
<FIELD name="ID_VERIFICATEUR" label="Vérificateur" titlelabel="Vérificateur" />
<FIELD name="ID_COMMERCIAL" label="Commercial" titlelabel="Commercial" />
<FIELD name="ID_COMPTE_PCE_SERVICE" label="Compte service"
titlelabel="Compte service" />
<FIELD name="COMPTE_SERVICE_AUTO" label="Gestion automatique du compte de service"
titlelabel="Gestion automatique du compte de service" />
<FIELD name="A_DEDUIRE" label="A déduire sur un prochain achat"
titlelabel="A déduire sur un prochain achat" />
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="ID_COMPTE_PCE_SERVICE" label="Compte service" titlelabel="Compte service" />
<FIELD name="COMPTE_SERVICE_AUTO" label="Gestion automatique du compte de service" titlelabel="Gestion automatique du compte de service" />
<FIELD name="A_DEDUIRE" label="A déduire sur un prochain achat" titlelabel="A déduire sur un prochain achat" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="MOTIF" label="Motif" titlelabel="Motif" />
<FIELD name="T_DEVISE" label="Total Devise" titlelabel="Total Devise" />
</TABLE>
 
<TABLE name="AVOIR_CLIENT_ELEMENT">
<FIELD name="NB_COLIS" label="Nb Colis" titlelabel="Nb Colis" />
<FIELD name="POIDS_COLIS_NET" label="Pds Colis" titlelabel="Pds Colis" />
<FIELD name="T_POIDS_COLIS_NET" label="Pds Colis Total" titlelabel="Pds Colis Total" />
<FIELD name="QTE_UNITAIRE" label="Qté U.V." titlelabel="Qté U.V." />
<FIELD name="ID_UNITE_VENTE" label="Unité de vente" titlelabel="Unité de vente" />
<FIELD name="ID_ARTICLE" label="Article" titlelabel="Article" />
268,22 → 223,17
<FIELD name="SERVICE" label="Service" titlelabel="Service" />
<FIELD name="PRIX_METRIQUE_VT_1" label="P.V. UV HT" titlelabel="P.V. UV HT" />
<FIELD name="PRIX_METRIQUE_HA_1" label="P.A. UV HT" titlelabel="P.A. UV HT" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut"
titlelabel="Longueur par défaut" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut" titlelabel="Longueur par défaut" />
<FIELD name="ID_METRIQUE_1" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV au metre HT"
titlelabel="PV au metre HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA au metre HT"
titlelabel="PHA au metre HT" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV au metre HT" titlelabel="PV au metre HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA au metre HT" titlelabel="PHA au metre HT" />
<FIELD name="VALEUR_METRIQUE_2" label="Largeur" titlelabel="Largeur" />
<FIELD name="ID_METRIQUE_2" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_3" label="PV au kg HT" titlelabel="PV au kg HT" />
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA au kg HT"
titlelabel="PHA au kg HT" />
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA au kg HT" titlelabel="PHA au kg HT" />
<FIELD name="VALEUR_METRIQUE_3" label="poids / m²" titlelabel="poids / m²" />
<FIELD name="ID_METRIQUE_3" label="Métrique" titlelabel="Métrique" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente"
titlelabel="Mode de vente" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente" titlelabel="Mode de vente" />
 
<FIELD name="ID_STYLE" label="Style" titlelabel="Style" />
<FIELD name="CODE_MISSION" label="Code mission" titlelabel="Code mission" />
290,40 → 240,29
<FIELD name="NOM_MISSION" label="Nom mission" titlelabel="Nom mission" />
<FIELD name="ACTIVITE" label="Activité" titlelabel="Activité" />
<FIELD name="SOUS_ACTIVITE" label="Sous activité" titlelabel="Sous activité" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente"
titlelabel="Mode de vente" />
<FIELD name="MONTANT_INITIAL" label="Montant initial"
titlelabel="Montant initial" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente" titlelabel="Mode de vente" />
<FIELD name="MONTANT_INITIAL" label="Montant initial" titlelabel="Montant initial" />
<FIELD name="INDICE_0" label="Indice 0" titlelabel="Indice 0" />
<FIELD name="INDICE_N" label="Indice N" titlelabel="Indice N" />
<FIELD name="Q18" label="Extension Q18" titlelabel="Extension Q18" />
<FIELD name="TARIF_Q18_HT" label="Tarif Q18 HT" titlelabel="Tarif Q18 HT" />
<FIELD name="OBJET_INSPECTE" label="Objet inspecté" titlelabel="Objet inspecté" />
<FIELD name="REFERENTIEL_INSPECTION" label="Référentiel inspection"
titlelabel="Référentiel inspection" />
<FIELD name="LOCAL_OBJET_INSPECTE" label="Localisation objet"
titlelabel="Localisation objet" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité"
titlelabel="Secteur d'activité" />
<FIELD name="ID_NATURE_MISSION" label="Nature de la mission"
titlelabel="Nature de la mission" />
<FIELD name="REFERENTIEL_INSPECTION" label="Référentiel inspection" titlelabel="Référentiel inspection" />
<FIELD name="LOCAL_OBJET_INSPECTE" label="Localisation objet" titlelabel="Localisation objet" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité" titlelabel="Secteur d'activité" />
<FIELD name="ID_NATURE_MISSION" label="Nature de la mission" titlelabel="Nature de la mission" />
<FIELD name="ID_DOMAINE" label="Domaine" titlelabel="Domaine" />
<FIELD name="MONTANT_REVISABLE" label="Révisable" titlelabel="Révisable" />
<FIELD name="INFOS_OBJET_INSPECTE" label="Commentaires objet"
titlelabel="Commentaires objet" />
<FIELD name="ID_SITE_INTERVENTION" label="Site d'intervention"
titlelabel="Site d'intervention" />
<FIELD name="INFOS_OBJET_INSPECTE" label="Commentaires objet" titlelabel="Commentaires objet" />
<FIELD name="ID_SITE_INTERVENTION" label="Site d'intervention" titlelabel="Site d'intervention" />
<FIELD name="ID_PERIODICITE" label="Périodicité" titlelabel="Périodicité" />
<FIELD name="DATE" label="Date d'intervention" titlelabel="Date d'intervention" />
<FIELD name="DATE_FIN" label="Date de fin d'intervention"
titlelabel="Date de fin d'intervention" />
<FIELD name="POURCENT_SERVICE" label="Pourcentage service"
titlelabel="Pourcentage service" />
<FIELD name="DATE_FIN" label="Date de fin d'intervention" titlelabel="Date de fin d'intervention" />
<FIELD name="POURCENT_SERVICE" label="Pourcentage service" titlelabel="Pourcentage service" />
</TABLE>
 
<TABLE name="AVOIR_FOURNISSEUR">
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
<FIELD name="DATE" label="Date" titlelabel="Date" />
<FIELD name="SOLDE" label="Soldé" titlelabel="Soldé" />
334,8 → 273,7
<FIELD name="NUMERO" label="Numéro" titlelabel="Numéro" />
<FIELD name="ID_COMPTE_PCE" label="Compte charges" titlelabel="Compte charges" />
<FIELD name="MONTANT_TVA" label="Total TVA" titlelabel="Total TVA" />
<FIELD name="A_DEDUIRE" label="A déduire sur un prochain achat"
titlelabel="A déduire sur un prochain achat" />
<FIELD name="A_DEDUIRE" label="A déduire sur un prochain achat" titlelabel="A déduire sur un prochain achat" />
<FIELD name="IMMO" label="Immobilisations" titlelabel="Immobilisations" />
</TABLE>
 
358,8 → 296,7
<FIELD name="IBAN" label="IBAN" titlelabel="IBAN" />
<FIELD name="CODE" label="Code" titlelabel="Code" />
<FIELD name="ID_JOURNAL" label="Journal" titlelabel="Journal" />
<FIELD name="AFFACTURAGE" label="Banque d'affacturage"
titlelabel="Banque d'affacturage" />
<FIELD name="AFFACTURAGE" label="Banque d'affacturage" titlelabel="Banque d'affacturage" />
<FIELD name="INFOS" label="Informations" titlelabel="Informations" />
</TABLE>
 
367,10 → 304,8
<FIELD name="DATE" label="Date" titlelabel="Date" />
<FIELD name="ID_FOURNISSEUR" label="Fournisseur" titlelabel="Fournisseur" />
<FIELD name="NUMERO" label="Numéro du bon" titlelabel="Numéro BR" />
<FIELD name="TOTAL_POIDS" label="Poids total (en kg)"
titlelabel="Poids total" />
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="TOTAL_POIDS" label="Poids total (en kg)" titlelabel="Poids total" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="TOTAL_HT" label="Total HT" titlelabel="Total HT" />
<FIELD name="TOTAL_TVA" label="Total TVA" titlelabel="Total TVA" />
<FIELD name="T_DEVISE" label="Total Devise" titlelabel="Total Devise" />
384,8 → 319,7
<FIELD name="QTE_UNITAIRE" label="Qté U.V." titlelabel="Qté U.V." />
<FIELD name="ID_UNITE_VENTE" label="Unité de vente" titlelabel="Unité de vente" />
<FIELD name="ID_ARTICLE" label="Article" titlelabel="Article" />
<FIELD name="ID_CODE_FOURNISSEUR" label="Code fournisseur"
titlelabel="Code fournisseur" />
<FIELD name="ID_CODE_FOURNISSEUR" label="Code fournisseur" titlelabel="Code fournisseur" />
<FIELD name="DESCRIPTIF" label="Descriptif" titlelabel="Descriptif" />
<FIELD name="CODE" label="Code" titlelabel="Code" />
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
400,18 → 334,14
<FIELD name="POIDS" label="Poids UV" titlelabel="Poids UV" />
<FIELD name="PRIX_METRIQUE_VT_1" label="P.V. UV HT" titlelabel="P.V. UV HT" />
<FIELD name="PRIX_METRIQUE_HA_1" label="PHA UV² HT" titlelabel="P.A. UV HT" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut"
titlelabel="Longueur par défaut" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut" titlelabel="Longueur par défaut" />
<FIELD name="ID_METRIQUE_1" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV au metre HT"
titlelabel="PV au metre HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA au metre HT"
titlelabel="PHA au metre HT" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV au metre HT" titlelabel="PV au metre HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA au metre HT" titlelabel="PHA au metre HT" />
<FIELD name="VALEUR_METRIQUE_2" label="Largeur" titlelabel="Largeur" />
<FIELD name="ID_METRIQUE_2" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_3" label="PV au kg HT" titlelabel="PV au kg HT" />
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA au kg HT"
titlelabel="PHA au kg HT" />
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA au kg HT" titlelabel="PHA au kg HT" />
<FIELD name="VALEUR_METRIQUE_3" label="poids / m²" titlelabel="poids / m²" />
<FIELD name="ID_METRIQUE_3" label="Métrique" titlelabel="Métrique" />
<FIELD name="T_PV_TTC" label="Total TTC" titlelabel="Total TTC" />
421,21 → 351,17
<FIELD name="T_POIDS" label="Poids total" titlelabel="Poids total" />
<FIELD name="ID_STYLE" label="Style" titlelabel="Style" />
<FIELD name="SERVICE" label="Service" titlelabel="Service" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente"
titlelabel="Mode de vente" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente" titlelabel="Mode de vente" />
</TABLE>
 
<TABLE name="BON_DE_LIVRAISON">
<FIELD name="DATE" label="Date" titlelabel="Date" />
<FIELD name="DATE_LIVRAISON" label="Date livraison" titlelabel="Date livraison" />
<FIELD name="ID_SAISIE_VENTE_FACTURE" label="Facture"
titlelabel="Facture" />
<FIELD name="ID_SAISIE_VENTE_FACTURE" label="Facture" titlelabel="Facture" />
<FIELD name="ID_CLIENT" label="Client" titlelabel="Client" />
<FIELD name="NUMERO" label="Numéro du bon" titlelabel="Numéro BL" />
<FIELD name="TOTAL_POIDS" label="Poids total (en kg)"
titlelabel="Poids total" />
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="TOTAL_POIDS" label="Poids total (en kg)" titlelabel="Poids total" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="TOTAL_HT" label="Total HT" titlelabel="Total HT" />
<FIELD name="TOTAL_TVA" label="Total TVA" titlelabel="Total TVA" />
<FIELD name="TOTAL_TTC" label="Total TTC" titlelabel="Total TTC" />
444,6 → 370,10
<FIELD name="T_DEVISE" label="Total Devise" titlelabel="Total Devise" />
</TABLE>
<TABLE name="BON_DE_LIVRAISON_ELEMENT">
 
<FIELD name="NB_COLIS" label="Nb Colis" titlelabel="Nb Colis" />
<FIELD name="POIDS_COLIS_NET" label="Pds Colis" titlelabel="Pds Colis" />
<FIELD name="T_POIDS_COLIS_NET" label="Pds Colis Total" titlelabel="Pds Colis Total" />
<FIELD name="QTE_UNITAIRE" label="Qté U.V." titlelabel="Qté U.V." />
<FIELD name="ID_UNITE_VENTE" label="Unité de vente" titlelabel="Unité de vente" />
<FIELD name="ID_ARTICLE" label="Article" titlelabel="Article" />
457,10 → 387,8
<FIELD name="ECOTAXE" label="Ecotaxe" titlelabel="Ecotaxe" />
<FIELD name="T_ECOTAXE" label="Dont écotaxe" titlelabel="Dont écotaxe" />
<FIELD name="ID_POCHETTE" label="Pochette" titlelabel="Pochette" />
<FIELD name="PRIX_FINAL_TTC" label="Prix de final de ttc"
titlelabel="Prix de final de ttc" />
<FIELD name="T_PRIX_FINAL_TTC" label="T de prix de final de ttc"
titlelabel="T de prix de final de ttc" />
<FIELD name="PRIX_FINAL_TTC" label="Prix de final de ttc" titlelabel="Prix de final de ttc" />
<FIELD name="T_PRIX_FINAL_TTC" label="T de prix de final de ttc" titlelabel="T de prix de final de ttc" />
<FIELD name="CODE" label="Code" titlelabel="Code" />
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
<FIELD name="PV_HT" label="PV Unitaire HT" titlelabel="PV Unitaire HT" />
472,18 → 400,14
<FIELD name="POIDS" label="Poids UV" titlelabel="Poids UV" />
<FIELD name="PRIX_METRIQUE_VT_1" label="P.V. UV HT" titlelabel="P.V. UV HT" />
<FIELD name="PRIX_METRIQUE_HA_1" label="P.A. UV HT" titlelabel="P.A. UV HT" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut"
titlelabel="Longueur par défaut" />
<FIELD name="VALEUR_METRIQUE_1" label="Longueur par défaut" titlelabel="Longueur par défaut" />
<FIELD name="ID_METRIQUE_1" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV au metre HT"
titlelabel="PV au metre HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA au metre HT"
titlelabel="PHA au metre HT" />
<FIELD name="PRIX_METRIQUE_VT_2" label="PV au metre HT" titlelabel="PV au metre HT" />
<FIELD name="PRIX_METRIQUE_HA_2" label="PHA au metre HT" titlelabel="PHA au metre HT" />
<FIELD name="VALEUR_METRIQUE_2" label="Largeur" titlelabel="Largeur" />
<FIELD name="ID_METRIQUE_2" label="Métrique" titlelabel="Métrique" />
<FIELD name="PRIX_METRIQUE_VT_3" label="PV au kg HT" titlelabel="PV au kg HT" />
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA au kg HT"
titlelabel="PHA au kg HT" />
<FIELD name="PRIX_METRIQUE_HA_3" label="PHA au kg HT" titlelabel="PHA au kg HT" />
<FIELD name="VALEUR_METRIQUE_3" label="poids / m²" titlelabel="poids / m²" />
<FIELD name="ID_METRIQUE_3" label="Métrique" titlelabel="Métrique" />
<FIELD name="T_PV_TTC" label="Total TTC" titlelabel="Total TTC" />
490,14 → 414,11
<FIELD name="T_PV_HT" label="Total Vt HT" titlelabel="Total Vt HT" />
<FIELD name="T_PA_HT" label="Total Ha HT" titlelabel="Total Ha HT" />
<FIELD name="T_POIDS" label="Poids total" titlelabel="Poids total" />
<FIELD name="T_POIDS_LIVREE" label="Poids total livré"
titlelabel="Poids total livré" />
<FIELD name="T_POIDS_LIVREE" label="Poids total livré" titlelabel="Poids total livré" />
<FIELD name="ID_STYLE" label="Style" titlelabel="Style" />
<FIELD name="SERVICE" label="Service" titlelabel="Service" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente"
titlelabel="Mode de vente" />
<FIELD name="T_PRIX_FINAL_TTC" label="Total prix client TTC"
titlelabel="Total prix client TTC" />
<FIELD name="ID_MODE_VENTE_ARTICLE" label="Mode de vente" titlelabel="Mode de vente" />
<FIELD name="T_PRIX_FINAL_TTC" label="Total prix client TTC" titlelabel="Total prix client TTC" />
<FIELD name="PRIX_FINAL_TTC" label="Prix client TTC" titlelabel="Prix client TTC" />
<FIELD name="T_ECOTAXE" label="Total Ecotaxe" titlelabel="Total Ecotaxe" />
</TABLE>
515,25 → 436,20
<FIELD name="ENCAISSE" label="Encaissé" titlelabel="Encaissé" />
<FIELD name="DATE_VENTE" label="Vente du" titlelabel="Vente du" />
<FIELD name="DATE_DEPOT" label="Déposé le" titlelabel="Déposé le" />
<FIELD name="DATE_MIN_DEPOT" label="A déposer après le"
titlelabel="A déposer aprés le" />
<FIELD name="DATE_MIN_DEPOT" label="A déposer après le" titlelabel="A déposer aprés le" />
<FIELD name="DATE" label="Daté du" titlelabel="Daté du" />
<FIELD name="ETS" label="Ets" titlelabel="Ets" />
<FIELD name="NUMERO" label="Numero" titlelabel="Numero" />
<FIELD name="REG_COMPTA" label="Régularisation en comptabilité"
titlelabel="Régularisation en comptabilité" />
<FIELD name="REG_COMPTA" label="Régularisation en comptabilité" titlelabel="Régularisation en comptabilité" />
</TABLE>
 
<TABLE name="CHEQUE_FOURNISSEUR">
<FIELD name="REG_COMPTA" label="Régularisation en comptabilité"
titlelabel="Régularisation en comptabilité" />
<FIELD name="ID_FOURNISSEUR" label="Numéro fournisseur"
titlelabel="Numéro fournisseur" />
<FIELD name="REG_COMPTA" label="Régularisation en comptabilité" titlelabel="Régularisation en comptabilité" />
<FIELD name="ID_FOURNISSEUR" label="Numéro fournisseur" titlelabel="Numéro fournisseur" />
<FIELD name="MONTANT" label="Montant" titlelabel="Montant" />
<FIELD name="DECAISSE" label="Décaissé" titlelabel="Décaissé" />
<FIELD name="DATE_ACHAT" label="Achat du" titlelabel="Achat du" />
<FIELD name="DATE_MIN_DECAISSE" label="A décaisser après le"
titlelabel="A décaisser après le" />
<FIELD name="DATE_MIN_DECAISSE" label="A décaisser après le" titlelabel="A décaisser après le" />
<FIELD name="DATE_DECAISSE" label="Décaissé le" titlelabel="Décaissé le" />
<FIELD name="DATE" label="Daté du" titlelabel="Daté du" />
<FIELD name="ETS" label="Ets" titlelabel="Ets" />
546,19 → 462,16
<FIELD name="DECAISSE" label="Décaissé" titlelabel="Décaissé" />
<FIELD name="DATE_AVOIR" label="Avoir du" titlelabel="Avoir du" />
<FIELD name="DATE_DECAISSE" label="Décaissé le" titlelabel="Décaissé le" />
<FIELD name="DATE_MIN_DECAISSE" label="A décaissé après le"
titlelabel="A décaissé après le" />
<FIELD name="DATE_MIN_DECAISSE" label="A décaissé après le" titlelabel="A décaissé après le" />
<FIELD name="DATE" label="Daté du" titlelabel="Daté du" />
<FIELD name="ETS" label="Ets" titlelabel="Ets" />
<FIELD name="NUMERO" label="Numero" titlelabel="Numero" />
<FIELD name="REG_COMPTA" label="Régularisation en comptabilité"
titlelabel="Régularisation en comptabilité" />
<FIELD name="REG_COMPTA" label="Régularisation en comptabilité" titlelabel="Régularisation en comptabilité" />
</TABLE>
 
<TABLE name="CLASSE_COMPTE">
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
<FIELD name="TYPE_NUMERO_COMPTE" label="Type numéro de compte"
titlelabel="Type numéro de compte" />
<FIELD name="TYPE_NUMERO_COMPTE" label="Type numéro de compte" titlelabel="Type numéro de compte" />
</TABLE>
 
<TABLE name="CLASSEMENT_CONVENTIONNEL">
575,8 → 488,7
<FIELD name="ID_PAYS" label="Pays" titlelabel="Pays" />
<FIELD name="ID_LANGUE" label="Langue" titlelabel="Langue" />
<FIELD name="ID_CLIENT" label="Client" titlelabel="Client" />
<FIELD name="FORME_JURIDIQUE" label="Forme juridique"
titlelabel="Forme juridique" />
<FIELD name="FORME_JURIDIQUE" label="Forme juridique" titlelabel="Forme juridique" />
<FIELD name="NOM" label="Nom client" titlelabel="Nom client" />
<FIELD name="CODE" label="Code client" titlelabel="Code client" />
<FIELD name="TEL" label="Téléphone" titlelabel="Téléphone" />
584,10 → 496,8
<FIELD name="FAX" label="Fax" titlelabel="Fax" />
<FIELD name="MAIL" label="Mail" titlelabel="Mail" />
<FIELD name="RESPONSABLE" label="Responsable" titlelabel="Responsable" />
<FIELD name="RESPONSABLE_TECH" label="Responsable tech."
titlelabel="Responsable tech." />
<FIELD name="RESPONSABLE_COM" label="Responsable comm."
titlelabel="Responsable comm." />
<FIELD name="RESPONSABLE_TECH" label="Responsable tech." titlelabel="Responsable tech." />
<FIELD name="RESPONSABLE_COM" label="Responsable comm." titlelabel="Responsable comm." />
<FIELD name="TEL_COM" label="Téléphone" titlelabel="Téléphone" />
<FIELD name="FAX_COM" label="Fax" titlelabel="Fax" />
<FIELD name="TEL_P_COM" label="Portable" titlelabel="Portable" />
596,36 → 506,26
<FIELD name="FAX_TECH" label="Fax" titlelabel="Fax" />
<FIELD name="TEL_P_TECH" label="Portable" titlelabel="Portable" />
<FIELD name="MAIL_TECH" label="E-mail" titlelabel="E-mail" />
<FIELD name="ID_COMPTE_PCE" label="Compte PCE associé"
titlelabel="Compte PCE associé" />
<FIELD name="ID_COMPTE_PCE" label="Compte PCE associé" titlelabel="Compte PCE associé" />
<FIELD name="ID_ADRESSE" label="Adr. Principale" titlelabel="Adr. Principale" />
<FIELD name="ID_ADRESSE_L" label="Adr. Livraison" titlelabel="Adr. Livraison" />
<FIELD name="ID_ADRESSE_F" label="Adr. Facturation" titlelabel="Adr. Facturation" />
<FIELD name="NUMERO_TVA" label="N° Intracommunautaire"
titlelabel="N° Intracom" />
<FIELD name="NUMERO_TVA" label="N° Intracommunautaire" titlelabel="N° Intracom" />
<FIELD name="ID_ADRESSE" label="Adresse" titlelabel="Adresse" />
<FIELD name="MARCHE_PUBLIC" label="Marché public" titlelabel="Marché public" />
<FIELD name="MARCHE_PRIVE" label="Marché privé" titlelabel="Marché privé" />
<FIELD name="ID_ADRESSE_L" label="Adresse de livraison"
titlelabel="Adresse livraison" />
<FIELD name="ID_ADRESSE_L" label="Adresse de livraison" titlelabel="Adresse livraison" />
<FIELD name="RIB" label="RIB" titlelabel="RIB" />
<FIELD name="SIRET" label="SIRET" titlelabel="SIRET" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité"
titlelabel="Secteur d'activité" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité" titlelabel="Secteur d'activité" />
<FIELD name="ID_BANQUE_POLE_PRODUIT" label="Banque" titlelabel="Banque" />
<FIELD name="ID_POLE_PRODUIT" label="Pôle produit" titlelabel="Pôle produit" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité"
titlelabel="Secteur d'activité" />
<FIELD name="INFOS" label="Informations complémentaires"
titlelabel="Informations complémentaires" />
<FIELD name="ID_MODE_REGLEMENT" label="Mode de règlement par défaut"
titlelabel="Mode de règlement par défaut" />
<FIELD name="AFFACTURAGE" label="Possibilité d'affacturage"
titlelabel="Possibilité d'affacturage" />
<FIELD name="MAX_FACTURE" label="Facturation limitée à"
titlelabel="Facturation limitée à" />
<FIELD name="COMPTANT" label="Règlement comptant obligatoire"
titlelabel="Règlement comptant obligatoire" />
<FIELD name="ID_SECTEUR_ACTIVITE" label="Secteur d'activité" titlelabel="Secteur d'activité" />
<FIELD name="INFOS" label="Informations complémentaires" titlelabel="Informations complémentaires" />
<FIELD name="ID_MODE_REGLEMENT" label="Mode de règlement par défaut" titlelabel="Mode de règlement par défaut" />
<FIELD name="AFFACTURAGE" label="Possibilité d'affacturage" titlelabel="Possibilité d'affacturage" />
<FIELD name="MAX_FACTURE" label="Facturation limitée à" titlelabel="Facturation limitée à" />
<FIELD name="COMPTANT" label="Règlement comptant obligatoire" titlelabel="Règlement comptant obligatoire" />
<FIELD name="ID_TARIF" label="Tarif" titlelabel="Tarif" />
</TABLE>
 
654,8 → 554,7
 
 
<TABLE name="CODE_MISSION">
<FIELD name="CONTROLE_TECHNIQUE" label="Mission controle technique"
titlelabel="Mission controle technique" />
<FIELD name="CONTROLE_TECHNIQUE" label="Mission controle technique" titlelabel="Mission controle technique" />
<FIELD name="CODE" label="Code" titlelabel="Code" />
<FIELD name="NOM" label="Libellé" titlelabel="Libellé" />
<FIELD name="ID_DOMAINE" label="Domaine" titlelabel="Domaine" />
662,10 → 561,8
<FIELD name="ID_SERVICE" label="Service" titlelabel="Service" />
<FIELD name="ACTIVITE" label="Activité" titlelabel="Activité" />
<FIELD name="SOUS_ACTIVITE" label="Sous activité" titlelabel="Sous activité" />
<FIELD name="ID_NATURE_MISSION" label="Nature mission"
titlelabel="Nature mission" />
<FIELD name="REFERENTIEL_INSPECTION" label="Référentiel d'inspection"
titlelabel="Référentiel d'inspection" />
<FIELD name="ID_NATURE_MISSION" label="Nature mission" titlelabel="Nature mission" />
<FIELD name="REFERENTIEL_INSPECTION" label="Référentiel d'inspection" titlelabel="Référentiel d'inspection" />
<FIELD name="NOTA" label="Nota" titlelabel="Nota" />
</TABLE>
 
689,10 → 586,8
<FIELD name="NOM" label="Nom du contact" titlelabel="Nom du contact" />
<FIELD name="ID_TITRE_PERSONNEL" label="Titre" titlelabel="Titre" />
<FIELD name="TEL_DIRECT" label="Téléphone" titlelabel="Téléphone" />
<FIELD name="TEL_PERSONEL" label="Téléphone Personnel"
titlelabel="Téléphone Personnel" />
<FIELD name="TEL_STANDARD" label="Téléphone du standard"
titlelabel="Téléphone du standard" />
<FIELD name="TEL_PERSONEL" label="Téléphone Personnel" titlelabel="Téléphone Personnel" />
<FIELD name="TEL_STANDARD" label="Téléphone du standard" titlelabel="Téléphone du standard" />
<FIELD name="TEL_MOBILE" label="Portable" titlelabel="Portable" />
<FIELD name="EMAIL" label="Mail" titlelabel="Mail" />
<FIELD name="FAX" label="Fax" titlelabel="Fax" />
705,10 → 600,8
<FIELD name="NOM" label="Nom du contact" titlelabel="Nom du contact" />
<FIELD name="ID_TITRE_PERSONNEL" label="Titre" titlelabel="Titre" />
<FIELD name="TEL_DIRECT" label="Téléphone" titlelabel="Téléphone" />
<FIELD name="TEL_PERSONEL" label="Téléphone Personnel"
titlelabel="Téléphone Personnel" />
<FIELD name="TEL_STANDARD" label="Téléphone du standard"
titlelabel="Téléphone du standard" />
<FIELD name="TEL_PERSONEL" label="Téléphone Personnel" titlelabel="Téléphone Personnel" />
<FIELD name="TEL_STANDARD" label="Téléphone du standard" titlelabel="Téléphone du standard" />
<FIELD name="TEL_MOBILE" label="Portable" titlelabel="Portable" />
<FIELD name="EMAIL" label="Mail" titlelabel="Mail" />
<FIELD name="FAX" label="Fax" titlelabel="Fax" />
721,10 → 614,8
<FIELD name="NOM" label="Nom du contact" titlelabel="Nom du contact" />
<FIELD name="ID_TITRE_PERSONNEL" label="Titre" titlelabel="Titre" />
<FIELD name="TEL_DIRECT" label="Téléphone" titlelabel="Téléphone" />
<FIELD name="TEL_PERSONEL" label="Téléphone Personnel"
titlelabel="Téléphone Personnel" />
<FIELD name="TEL_STANDARD" label="Téléphone du standard"
titlelabel="Téléphone du standard" />
<FIELD name="TEL_PERSONEL" label="Téléphone Personnel" titlelabel="Téléphone Personnel" />
<FIELD name="TEL_STANDARD" label="Téléphone du standard" titlelabel="Téléphone du standard" />
<FIELD name="TEL_MOBILE" label="Portable" titlelabel="Portable" />
<FIELD name="EMAIL" label="Mail" titlelabel="Mail" />
<FIELD name="FAX" label="Fax" titlelabel="Fax" />
734,31 → 625,20
 
<TABLE name="CONTRAT_SALARIE">
<FIELD name="NATURE" label="Nature de l'emploi (*)" titlelabel="Nature de l'emploi" />
<FIELD name="ID_CODE_EMPLOI" label="Catégorie socioprofessionnelle "
titlelabel="Code Catégorie socioprofessionnelle" />
<FIELD name="ID_CODE_CONTRAT_TRAVAIL" label="Contrat de travail"
titlelabel="Code contrat" />
<FIELD name="ID_CODE_DROIT_CONTRAT" label="Droit du contrat de travail"
titlelabel="Code droit du contrat" />
<FIELD name="ID_CODE_STATUT_PROF" label="Statut professionnel du salarié"
titlelabel="Code statut professionnel" />
<FIELD name="ID_CODE_STATUT_CATEGORIEL" label="Statut catégoriel du salarié "
titlelabel="Code statut catégoriel" />
<FIELD name="ID_CODE_CARACT_ACTIVITE" label="Caractéristique de l'activité"
titlelabel="Code caractéristique" />
<FIELD name="ID_CODE_STATUT_CAT_CONV" label="Statut catégoriel conventionnel"
titlelabel="Statut catégoriel conventionnel" />
<FIELD name="ID_CODE_EMPLOI" label="Catégorie socioprofessionnelle " titlelabel="Code Catégorie socioprofessionnelle" />
<FIELD name="ID_CODE_CONTRAT_TRAVAIL" label="Contrat de travail" titlelabel="Code contrat" />
<FIELD name="ID_CODE_DROIT_CONTRAT" label="Droit du contrat de travail" titlelabel="Code droit du contrat" />
<FIELD name="ID_CODE_STATUT_PROF" label="Statut professionnel du salarié" titlelabel="Code statut professionnel" />
<FIELD name="ID_CODE_STATUT_CATEGORIEL" label="Statut catégoriel du salarié " titlelabel="Code statut catégoriel" />
<FIELD name="ID_CODE_CARACT_ACTIVITE" label="Caractéristique de l'activité" titlelabel="Code caractéristique" />
<FIELD name="ID_CODE_STATUT_CAT_CONV" label="Statut catégoriel conventionnel" titlelabel="Statut catégoriel conventionnel" />
 
<FIELD name="CODE_IRC_UGRR" label="Code IRC UGRR" titlelabel="Code IRC UGRR" />
<FIELD name="CODE_IRC_UGRC" label="Code IRC UGRC" titlelabel="Code IRC UGRC" />
<FIELD name="NUMERO_RATTACHEMENT_UGRR" label="N° rattachement UGRR"
titlelabel="N° rattachement UGRR" />
<FIELD name="NUMERO_RATTACHEMENT_UGRC" label="N° rattachement UGRC"