OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 80 → Rev 81

/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.xml
New file
0,0 → 1,166
<?xml version="1.0" encoding="UTF-8" ?>
 
<contentDocument>
 
<element location="B1" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="TYPE"/>
<field base="Common" table="SOCIETE_COMMON" name="NOM"/>
</element>
 
<element location="B2" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="ID_ADRESSE_COMMON">
<field base="Common" table="ADRESSE_COMMON" name="RUE"/>
</field>
</element>
 
<element location="B3" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="ID_ADRESSE_COMMON">
<field base="Common" table="ADRESSE_COMMON" name="VILLE" type="villeCP"/>
<field base="Common" table="ADRESSE_COMMON" name="VILLE" type="ville"/>
</field>
</element>
<element location="B7" type="replace" replacePattern="_">
<field base="Common" table="SOCIETE_COMMON" name="NUM_NII"/>
</element>
<element location="B8" type="replace" replacePattern="_">
<field base="Common" table="SOCIETE_COMMON" name="NUM_TEL"/>
</element>
<element location="B9" type="replace" replacePattern="_">
<field base="Common" table="SOCIETE_COMMON" name="NUM_FAX"/>
</element>
<element location="B10" type="replace" replacePattern="_">
<field base="Common" table="SOCIETE_COMMON" name="MAIL"/>
</element>
<element location="B13" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="NUMERO"/>
</element>
<element location="C13" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="DATE"/>
</element>
<element type="invoice_paid_amount" location="L65"/>
<element location="B16" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_COMMERCIAL">
<field base="Societe" table="COMMERCIAL" name="PRENOM" type="Initiale" suffix="."/>
<field base="Societe" table="COMMERCIAL" name="NOM"/>
</field>
</element>
 
<element location="C16" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="NOM"/>
</element>
<element location="H10" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_CLIENT">
<field base="Societe" table="CLIENT" name="FORME_JURIDIQUE"/>
<field base="Societe" table="CLIENT" name="NOM"/>
</field>
</element>
<element location="H11" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_CLIENT">
<field base="Societe" table="CLIENT" name="ID_ADRESSE">
<field base="Societe" table="ADRESSE" name="RUE"/>
</field>
</field>
</element>
<element location="H13" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_CLIENT">
<field base="Societe" table="CLIENT" name="ID_ADRESSE">
<field base="Societe" table="ADRESSE" name="VILLE" type="villeCP"/>
<field base="Societe" table="ADRESSE" name="VILLE" type="ville"/>
<field base="Societe" table="ADRESSE" name="CEDEX" prefix="CEDEX " conditionField="HAS_CEDEX"/>
</field>
</field>
</element>
 
 
<element type="sales.account.label" location="B58"/>
<element type="sales.account.command.total" location="J58"/>
 
<element location="L58" type="sales.partial.value"/>
 
<element type="sales.account.total" location="L61"/>
 
<element location="L62" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="T_HT" type="devise"/>
</element>
<element location="L65" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="T_TTC" type="devise"/>
</element>
 
<element location="B63" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_MODE_REGLEMENT">
 
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Règlement souhaité" conditionField="COMPTANT" conditionExpValue="true" display="false"/>
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Facture acquitée par" conditionField="COMPTANT" conditionExpValue="false" display="false"/>
 
<field base="Societe" table="MODE_REGLEMENT" name="ID_TYPE_REGLEMENT">
<field base="Societe" table="TYPE_REGLEMENT" name="NOM" valuesExpected="Indéfini"/>
</field>
<field base="Societe" table="MODE_REGLEMENT" name="NOM"/>
<field base="Societe" table="MODE_REGLEMENT" name="DATE_VIREMENT" prefix="Le " valuesExpected="" type="Date" datePattern="dd/MM/yy"/>
<field base="Societe" table="MODE_REGLEMENT" name="NUMERO" prefix="N° " valuesExpected=""/>
<field base="Societe" table="MODE_REGLEMENT" name="ETS" prefix="Banque " valuesExpected=""/>
</field>
</element>
 
<element location="B64" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="DATE" type="DateEcheance" prefix="Règlement de cette facture au plus tard le " valuesExpected=" "/>
</element>
 
<element location="B64" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_MODE_REGLEMENT">
 
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Règlement à date de réception de facture" conditionField="COMPTANT" conditionExpValue="false" display="false"/>
</field>
</element>
 
<table endPageLine="65" firstLine="63" endLine="65" lastColumn="I" base="Societe" table="TVA">
<element location="I" name="NOM" prefix="Total ">
</element>
 
<element location="L" type="Devise" name="MONTANT_TVA">
</element>
 
</table>
 
<table endPageLine="65" firstLine="20" endLine="54" blankLineBeforeStyle="Titre 1,Titre 2" lastColumn="K" base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT">
<element location="B" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" name="NOM"/>
</element>
 
<element location="I" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" name="PV_HT" valuesExpected="0" type="devise"/>
</element>
 
<element location="J" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" name="QTE" conditionField="PV_HT" conditionExpValue="0"/>
</element>
<element location="K" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" name="ID_TAXE" conditionField="PV_HT" conditionExpValue="0">
<field base="Societe" table="TAXE" name="TAUX" op="/" number="100.0"/>
</field>
</element>
 
<element location="L" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" valuesExpected="0" name="T_PV_HT" type="devise"/>
</element>
</table>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.ods
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.odsp
New file
0,0 → 1,9
<odsp>
<spliteveryrow>
<sheet number="0">65</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
 
 
</odsp>
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/itemview/SimpleRowItemView.java
13,7 → 13,6
package org.openconcerto.sql.sqlobject.itemview;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.ui.valuewrapper.ValueWrapper;
 
23,14 → 22,11
super(wrapper);
}
 
public SQLField getField() {
return this.getFields().get(0);
}
 
protected final SQLTable getTable() {
return this.getField().getTable();
}
 
@Override
public void setEditable(boolean b) {
if (this.getComp() != null)
this.getComp().setEnabled(b);
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/itemview/BaseRowItemView.java
29,6 → 29,15
*/
public abstract class BaseRowItemView implements MutableRowItemView {
 
static public final <T> T getOnlyOne(final List<T> l) {
if (l.size() == 0)
return null;
else if (l.size() > 1)
throw new IllegalStateException("More than one : " + l);
else
return l.get(0);
}
 
private final List<SQLField> fields;
 
private String sqlName;
50,11 → 59,17
 
protected abstract void init();
 
protected final List<SQLField> getFields() {
@Override
public final List<SQLField> getFields() {
return this.fields;
}
 
@Override
public final SQLField getField() {
return getOnlyOne(this.fields);
}
 
@Override
public String toString() {
return this.getClass().getName() + " on " + this.getFields();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/itemview/VWRowItemView.java
47,6 → 47,7
return this.wrapper;
}
 
@Override
protected void init() {
this.helper = this.createHelper();
}
73,10 → 74,13
return EmptyObjFromVO.getDefaultPredicate();
}
 
@Override
public void resetValue() {
this.getWrapper().resetValue();
}
 
// not final to allow subclass without exactly one field
@Override
public void show(SQLRowAccessor r) {
if (r.getFields().contains(this.getField().getName())) {
@SuppressWarnings("unchecked")
89,10 → 93,13
}
}
 
// not final to allow subclass without exactly one field
@Override
public void update(SQLRowValues vals) {
vals.put(this.getField().getName(), this.isEmpty() ? SQLRowValues.SQL_DEFAULT : this.getWrapper().getValue());
}
 
@Override
public final void addValueListener(PropertyChangeListener l) {
this.getWrapper().addValueListener(l);
}
126,6 → 133,7
return this.getWrapper().getValidState();
}
 
@Override
public final void addValidListener(ValidListener l) {
this.getWrapper().addValidListener(new ChainValidListener(this, l));
}
135,6 → 143,7
this.getWrapper().removeValidListener(new ChainValidListener(this, l));
}
 
@Override
public final Component getComp() {
return this.getWrapper().getComp();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/itemview/PolyComboRIV.java
26,10 → 26,6
super(new ValueWrapperFromVO<SQLRow>(poly));
}
 
public SQLField getField() {
return this.getFields().get(0);
}
 
public void resetValue() {
this.getPolyCombo().resetValue();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLRowView.java
368,8 → 368,7
public Collection<SQLField> getAllFields() {
final Set<SQLField> res = new HashSet<SQLField>();
for (final SQLRowItemView view : this.getViewsFast()) {
if (view.getField() != null)
res.add(view.getField());
res.addAll(view.getFields());
}
return res;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/UpdateBuilder.java
63,10 → 63,14
}
 
private final void checkField(final String field) {
if (!this.getTable().contains(field))
throw new IllegalArgumentException("unknown " + field + " in " + this.getTable().getSQLName());
checkField(field, getTable());
}
 
private final void checkField(final String field, final TableRef t) {
if (!t.getTable().contains(field))
throw new IllegalArgumentException("unknown " + field + " in " + t.getSQL());
}
 
private final void checkField(final SQLField field) {
if (this.getTable() != field.getTable())
throw new IllegalArgumentException(field + " not in " + this.getTable().getSQLName());
175,10 → 179,16
this.tables.add(definition + (rawAlias == null ? "" : " " + rawAlias));
}
 
public final void addVirtualJoin(final TableRef t, final String joinedTableField) {
public final void addBackwardVirtualJoin(final TableRef t, final String joinedTableField) {
checkField(joinedTableField, t);
this.addVirtualJoin(t.getSQL(), t.getAlias(), true, joinedTableField, getTable().getKey().getName());
}
 
public final void addForwardVirtualJoin(final TableRef t, final String joinField) {
checkField(joinField, getTable());
this.addVirtualJoin(t.getSQL(), t.getAlias(), true, t.getKey().getField().getName(), joinField);
}
 
public final void addVirtualJoin(final String definition, final String alias, final String joinedTableField) {
this.addVirtualJoin(definition, alias, false, joinedTableField, getTable().getKey().getName());
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLRowItemView.java
21,6 → 21,7
 
import java.awt.Component;
import java.beans.PropertyChangeListener;
import java.util.List;
 
/**
* An element of a SQLRowView, it can be either a field (DESIGNATION) or not.
32,9 → 33,11
// eg DESIGNATION, OBSERVATIONS
public String getSQLName();
 
// TODO renvoyer un Set
// null if no fields, Exception if more than one
public SQLField getField();
 
public List<SQLField> getFields();
 
// TODO rename en reset()
public void resetValue();
 
/trunk/OpenConcerto/src/org/openconcerto/sql/element/RowBacked.java
146,7 → 146,7
}
 
public RowBacked getParent() {
final SQLRowAccessor parent = this.getRow().getForeign(this.getElement().getParentForeignField());
final SQLRowAccessor parent = this.getRow().getForeign(this.getElement().getParentForeignFieldName());
return (RowBacked) getModelObject(parent);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElement.java
840,13 → 840,21
return this.sharedFF;
}
 
public final synchronized String getParentForeignField() {
public final SQLField getParentForeignField() {
return getOptionalField(this.getParentForeignFieldName());
}
 
public final synchronized String getParentForeignFieldName() {
this.initFF();
return this.parentFF;
}
 
private final SQLField getParentFF() {
final String name = getParentFFName();
return getOptionalField(getParentFFName());
}
 
// optional but if specified it must exist
private final SQLField getOptionalField(final String name) {
return name == null ? null : this.getTable().getField(name);
}
 
865,10 → 873,10
}
 
public final SQLElement getParentElement() {
if (this.getParentForeignField() == null)
if (this.getParentForeignFieldName() == null)
return null;
else
return this.getForeignElement(this.getParentForeignField());
return this.getForeignElement(this.getParentForeignFieldName());
}
 
private final synchronized Map<String, SQLElement> getPrivateFF() {
946,7 → 954,7
// *** rf and ff
 
/**
* The links towards the parents (either {@link #getParentForeignField()} or
* The links towards the parents (either {@link #getParentForeignFieldName()} or
* {@link #getPrivateParentReferentFields()}) of this element.
*
* @return the graph links towards the parents of this element.
957,8 → 965,9
final DatabaseGraph graph = this.getTable().getDBSystemRoot().getGraph();
for (final SQLField refField : refFields)
res.add(graph.getForeignLink(refField));
if (this.getParentForeignField() != null)
res.add(graph.getForeignLink(this.getTable().getField(getParentForeignField())));
final SQLField parentFF = this.getParentForeignField();
if (parentFF != null)
res.add(graph.getForeignLink(parentFF));
return res;
}
 
1354,10 → 1363,10
}
// si on a spécifié un parent, eg BATIMENT[23]
if (parent != null) {
final SQLTable foreignTable = this.getTable().getBase().getGraph().getForeignTable(this.getTable().getField(this.getParentForeignField()));
final SQLTable foreignTable = this.getParentForeignField().getForeignTable();
if (!parent.getTable().equals(foreignTable))
throw new IllegalArgumentException(parent + " is not a parent of " + row);
copy.put(this.getParentForeignField(), parent.getID());
copy.put(this.getParentForeignFieldName(), parent.getID());
}
 
return copy;
1375,8 → 1384,8
check(row);
vals.setAll(row.getAllValues());
vals.load(row, this.getNormalForeignFields());
if (this.getParentForeignField() != null)
vals.put(this.getParentForeignField(), row.getObject(this.getParentForeignField()));
if (this.getParentForeignFieldName() != null)
vals.put(this.getParentForeignFieldName(), row.getObject(this.getParentForeignFieldName()));
vals.load(row, this.getSharedForeignFields());
}
 
1474,7 → 1483,7
// a foreign row (which isn't the case for private)
private SQLRow getForeignParent(SQLRow row, final SQLRowMode mode) {
check(row);
return this.getParentForeignField() == null ? null : row.getForeignRow(this.getParentForeignField(), mode);
return this.getParentForeignFieldName() == null ? null : row.getForeignRow(this.getParentForeignFieldName(), mode);
}
 
// {SQLField => List<SQLRow>}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/BaseSQLComponent.java
20,6 → 20,7
import org.openconcerto.sql.Log;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFieldsSet;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
215,7 → 216,7
final SQLType type = getField(field).getType();
if (getField(field).isKey()) {
// foreign
comp = createValueWrapper(new ElementComboBox(), type, wantedType);
comp = createSubValueWrapper(new ElementComboBox(), type, wantedType);
} else {
if (Boolean.class.isAssignableFrom(type.getJavaType())) {
// TODO hack to view the focus (should try to paint around the button)
231,12 → 232,12
panel.add(cb, BorderLayout.LINE_START);
comp = addValidatedValueWrapper(castVW(new BooleanValueWrapper(panel, cb), Boolean.class, wantedType), type);
} else if (Date.class.isAssignableFrom(type.getJavaType())) {
comp = createValueWrapper(new JDate(), type, wantedType);
comp = createSubValueWrapper(new JDate(), type, wantedType);
} else if (String.class.isAssignableFrom(type.getJavaType()) && type.getSize() >= 512) {
comp = createValueWrapper(new SQLSearchableTextCombo(ComboLockedMode.UNLOCKED, true), type, wantedType);
comp = createSubValueWrapper(new SQLSearchableTextCombo(ComboLockedMode.UNLOCKED, true), type, wantedType);
} else {
// regular
comp = createValueWrapper(new SQLTextCombo(), type, wantedType);
comp = createSubValueWrapper(new SQLTextCombo(), type, wantedType);
}
}
comp.getComp().setOpaque(false);
279,7 → 280,7
throw new NullPointerException("comp for " + field + " is null");
if (comp instanceof MutableRowItemView)
throw new IllegalStateException("Comp is a MutableRowItemView, creating a SimpleRowItemView would ignore its methods : " + comp);
return createRowItemView(createValueWrapper(comp, field.getType(), Object.class));
return createRowItemView(createSubValueWrapper(comp, field.getType(), Object.class));
}
 
// just to make javac happy (type parameter for SimpleRowItemView)
289,13 → 290,23
return new SimpleRowItemView<T>(vw);
}
 
static private <T> ValueWrapper<? extends T> createValueWrapper(JComponent comp, final SQLType type, final Class<T> wantedType) {
// useful when the exact type of field is known (e.g. String) and setValue() is needed
static public final <T> ValueWrapper<T> createValueWrapper(JComponent comp, final SQLType type, final Class<T> wantedType) {
// equality allow returned ValueWrapper to both produce and consume T
if (!wantedType.equals(type.getJavaType()))
throw new ClassCastException("wanted type " + wantedType + " is not " + type);
final ValueWrapper<T> res = ValueWrapperFactory.create(comp, wantedType);
return addValidatedValueWrapper(res, type);
}
 
// useful when the exact type of field isn't known (e.g. Number) and only getValue() is needed
static public final <T> ValueWrapper<? extends T> createSubValueWrapper(JComponent comp, final SQLType type, final Class<T> wantedSuperType) {
final Class<?> fieldClass = type.getJavaType();
ValueWrapper<? extends T> res = ValueWrapperFactory.create(comp, fieldClass.asSubclass(wantedType));
final ValueWrapper<? extends T> res = ValueWrapperFactory.create(comp, fieldClass.asSubclass(wantedSuperType));
return addValidatedValueWrapper(res, type);
}
 
static private <T> ValueWrapper<? extends T> addValidatedValueWrapper(ValueWrapper<? extends T> res, final SQLType type) {
static private <T> ValueWrapper<T> addValidatedValueWrapper(ValueWrapper<T> res, final SQLType type) {
final Class<?> fieldClass = type.getJavaType();
if (String.class.isAssignableFrom(fieldClass)) {
res = ValidatedValueWrapper.add(res, new ITransformer<T, ValidState>() {
369,9 → 380,8
final Spec spec = SpecParser.create(specObj);
 
// ParentForeignField is always required
final String fieldName = v.getField().getName();
final Set<String> reqNames = this.getRequiredNames();
if (spec.isRequired() || fieldName.equals(getElement().getParentForeignField()) || reqNames == null || reqNames.contains(v.getSQLName())) {
if (spec.isRequired() || v.getFields().contains(getElement().getParentForeignField()) || reqNames == null || reqNames.contains(v.getSQLName())) {
this.required.add(v);
if (v instanceof ElementSQLObject)
((ElementSQLObject) v).setRequired(true);
388,10 → 398,10
// - but still wait the longest to be sure the RIV is initialized (SQLComponentItem)
v.resetValue();
 
if (!this.hide.contains(v.getField())) {
if (!CollectionUtils.containsAny(this.hide, v.getFields())) {
if (spec.isAdditional()) {
if (this.additionalFieldsPanel == null)
Log.get().warning("No additionalFieldsPanel for " + v.getField() + " : " + v);
Log.get().warning("No additionalFieldsPanel for " + v.getFields() + " : " + v);
else
this.additionalFieldsPanel.add(getDesc(v), v.getComp());
} else {
409,8 → 419,9
}
 
private boolean dontEdit(SQLRowItemView v) {
final String fieldName = v.getField().getName();
return this.getElement().getReadOnlyFields().contains(fieldName) || (this.getMode() != Mode.INSERTION && this.getElement().getInsertOnlyFields().contains(fieldName));
final Set<String> fieldsNames = new SQLFieldsSet(v.getFields()).getFieldsNames(getTable());
return CollectionUtils.containsAny(this.getElement().getReadOnlyFields(), fieldsNames)
|| (this.getMode() != Mode.INSERTION && CollectionUtils.containsAny(this.getElement().getInsertOnlyFields(), fieldsNames));
}
 
protected final void inited() {
/trunk/OpenConcerto/src/org/openconcerto/sql/element/UISQLComponent.java
134,7 → 134,7
}
 
private String getDefaultWhere(SQLRowItemView obj) {
if (getElement().getPrivateForeignFields().contains(obj.getField().getName()))
if (obj.getFields().size() == 1 && getElement().getPrivateForeignFields().contains(obj.getField().getName()))
return "bordered";
return null;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/BaseSQLObject.java
19,7 → 19,10
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.request.MutableRowItemView;
import org.openconcerto.sql.sqlobject.itemview.BaseRowItemView;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
 
import javax.swing.JPanel;
29,7 → 32,7
*/
public abstract class BaseSQLObject extends JPanel implements MutableRowItemView {
 
private SQLField field;
private List<SQLField> fields;
private String sqlName;
 
public BaseSQLObject() {
40,7 → 43,7
@Override
public void init(String sqlName, Set<SQLField> fields) {
this.sqlName = sqlName;
this.field = fields.iterator().next();
this.fields = new ArrayList<SQLField>(fields);
}
 
@Override
50,9 → 53,14
 
@Override
public final SQLField getField() {
return this.field;
return BaseRowItemView.getOnlyOne(this.fields);
}
 
@Override
public List<SQLField> getFields() {
return this.fields;
}
 
public SQLTable getTable() {
return this.getField().getTable();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/RowsSQLListModel.java
19,6 → 19,7
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.IResultSetHandler;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
46,15 → 47,15
@Override
protected void reload(final boolean noCache) {
final Set<Number> ids = getIds();
final String key = this.getElement().getParentForeignField();
final SQLField key = this.getElement().getParentForeignField();
 
final SQLSelect sel = new SQLSelect(this.getElement().getTable().getBase());
final SQLSelect sel = new SQLSelect();
sel.addSelectStar(this.getElement().getTable());
sel.addOrderSilent(this.getElement().getTable().getName());
 
// si null pas de where, on montre tout
if (ids != null && key != null) {
sel.setWhere(new Where(this.getElement().getTable().getField(key), ids));
sel.setWhere(new Where(key, ids));
}
final SQLDataSource source = this.getElement().getTable().getBase().getDataSource();
 
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/ElementsSQLListModel.java
57,9 → 57,9
 
private final int getCount(SQLElement elem) {
if (!this.counts.containsKey(elem)) {
final SQLSelect sel = new SQLSelect(elem.getTable().getBase());
final SQLSelect sel = new SQLSelect();
sel.addSelectFunctionStar("count");
sel.setWhere(new Where(elem.getTable().getField(elem.getParentForeignField()), this.getIds()));
sel.setWhere(new Where(elem.getParentForeignField(), this.getIds()));
final Number count = (Number) elem.getTable().getBase().getDataSource().executeScalar(sel.asString());
this.counts.put(elem, count);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/FieldExpander.java
171,7 → 171,7
return Collections.emptyList();
// MAYBE check that all fields belong to the same path
final Path fieldsPath = fieldsOrig.get(0).getPath();
final String parentFF = Configuration.getInstance().getDirectory().getElement(fieldsPath.getLast()).getParentForeignField();
final SQLField parentFF = Configuration.getInstance().getDirectory().getElement(fieldsPath.getLast()).getParentForeignField();
 
final List<Tuple2<Path, List<FieldPath>>> res = new ArrayList<Tuple2<Path, List<FieldPath>>>();
final List<FieldPath> currentL = new ArrayList<FieldPath>();
178,7 → 178,7
res.add(Tuple2.create(fieldsPath, currentL));
IFieldPath parent = null;
for (final IFieldPath f : fieldsOrig) {
if (f.getField().getName().equals(parentFF))
if (f.getField().equals(parentFF))
parent = f;
else
currentL.addAll(this.expand(f));
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLField.java
422,4 → 422,8
return Path.get(getTable());
}
 
@Override
public String getFieldName() {
return this.getName();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesCluster.java
19,11 → 19,11
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;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.Matrix;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.cc.Closure;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
32,7 → 32,6
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EventObject;
240,7 → 239,7
// and link them together
for (final SQLRowValues n : this.getItems()) {
// use referents instead of foreigns to copy order
for (final Entry<SQLField, Collection<SQLRowValues>> e : n.getReferents().entrySet())
for (final Entry<SQLField, Set<SQLRowValues>> e : n.getReferents().entrySet())
for (final SQLRowValues ref : e.getValue()) {
noLinkCopy.get(ref).put(e.getKey().getName(), noLinkCopy.get(n));
}
451,11 → 450,11
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;
final SetMap<SQLField, SQLRowValues> nextVals;
if (actualDirection == Direction.FOREIGN) {
final Map<SQLField, SQLRowValues> foreigns = current.getForeignsBySQLField();
nextVals = new CollectionMap<SQLField, SQLRowValues>(new ArrayList<SQLRowValues>(), foreigns.size());
nextVals.putAll(foreigns);
nextVals = new SetMap<SQLField, SQLRowValues>(foreigns.size());
nextVals.mergeScalarMap(foreigns);
} else {
assert actualDirection == Direction.REFERENT;
nextVals = current.getReferents();
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValues.java
26,12 → 26,13
import org.openconcerto.sql.request.Inserter.ReturnMode;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.utils.ReOrder;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CopyUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
151,9 → 152,9
 
private final Map<String, Object> values;
private final Map<String, SQLRowValues> foreigns;
private final CollectionMap<SQLField, SQLRowValues> referents;
private final SetMap<SQLField, SQLRowValues> referents;
private SQLRowValuesCluster graph;
private CollectionMap<SQLField, ReferentChangeListener> referentsListener;
private ListMap<SQLField, ReferentChangeListener> referentsListener;
 
public SQLRowValues(SQLTable t) {
super(t);
161,12 → 162,9
// don't use value too low for initialCapacity otherwise rehash operations
this.values = new LinkedHashMap<String, Object>(8);
this.foreigns = new HashMap<String, SQLRowValues>(4);
// extend (rather than use another constructor) for performance
// eg 20% gained when fetching 45000 rows
this.referents = new CollectionMap<SQLField, SQLRowValues>(4) {
@SuppressWarnings("unchecked")
this.referents = new SetMap<SQLField, SQLRowValues>(4, org.openconcerto.utils.CollectionMap2.Mode.NULL_FORBIDDEN, false) {
@Override
public Collection<SQLRowValues> createCollection(Collection coll) {
public Set<SQLRowValues> createCollection(Collection<? extends SQLRowValues> coll) {
// use LinkedHashSet so that the order is preserved, eg we can iterate over LOCALs
// pointing to a BATIMENT with consistent and predictable (insertion-based) order.
// use IdentitySet to be able to put two equal instances
240,7 → 238,7
}
if (newRowVals) {
final SQLRowValues vals = (SQLRowValues) value;
vals.referents.put(f, this);
vals.referents.add(f, this);
this.foreigns.put(fieldName, vals);
this.graph.add(this, f, vals);
assert this.graph == vals.graph;
342,7 → 340,7
});
}
 
final CollectionMap<SQLField, SQLRowValues> getReferents() {
final SetMap<SQLField, SQLRowValues> getReferents() {
return this.referents;
}
 
351,7 → 349,7
// remove the backdoor since values() returns a view
// remove duplicates (e.g. this is a CONTACT referenced by ID_CONTACT_RAPPORT &
// ID_CONTACT_RDV from the same site)
return this.referents.createCollection(this.referents.values());
return this.referents.createCollection(this.referents.allValues());
}
 
@Override
364,7 → 362,7
// remove duplicates
final Collection<SQLRowValues> res = this.referents.createCollection(null);
assert res.isEmpty();
for (final Map.Entry<SQLField, Collection<SQLRowValues>> e : this.referents.entrySet()) {
for (final Map.Entry<SQLField, Set<SQLRowValues>> e : this.referents.entrySet()) {
if (e.getKey().getTable().equals(refTable))
res.addAll(e.getValue());
}
395,7 → 393,7
private final SQLRowValues changeReferents(final SQLField f, final boolean retain) {
if (f != null || !retain) {
// copy otherwise ConcurrentModificationException
for (final Entry<SQLField, Collection<SQLRowValues>> e : CopyUtils.copy(this.getReferents()).entrySet()) {
for (final Entry<SQLField, Set<SQLRowValues>> e : CopyUtils.copy(this.getReferents()).entrySet()) {
if (f == null || e.getKey().equals(f) != retain) {
for (final SQLRowValues ref : e.getValue()) {
ref.put(e.getKey().getName(), null);
413,7 → 411,7
public SQLRowValues retainReferents(Collection<SQLRowValues> toRetain) {
toRetain = CollectionUtils.toIdentitySet(toRetain);
// copy otherwise ConcurrentModificationException
for (final Entry<SQLField, Collection<SQLRowValues>> e : CopyUtils.copy(this.getReferents()).entrySet()) {
for (final Entry<SQLField, Set<SQLRowValues>> e : CopyUtils.copy(this.getReferents()).entrySet()) {
for (final SQLRowValues ref : e.getValue()) {
if (!toRetain.contains(ref))
ref.put(e.getKey().getName(), null);
929,8 → 927,8
*/
public final void addReferentListener(SQLField field, ReferentChangeListener l) {
if (this.referentsListener == null)
this.referentsListener = new CollectionMap<SQLField, ReferentChangeListener>(new ArrayList<ReferentChangeListener>());
this.referentsListener.put(field, l);
this.referentsListener = new ListMap<SQLField, ReferentChangeListener>();
this.referentsListener.add(field, l);
}
 
public final void removeReferentListener(SQLField field, ReferentChangeListener l) {
1601,18 → 1599,6
return new SQLTableListenerData<SQLRowValues>(this, l);
}
 
public String dumpValues() {
String result = "[";
result += CollectionUtils.join(this.values.entrySet(), ", ", new ITransformer<Entry<String, ?>, String>() {
public String transformChecked(final Entry<String, ?> e) {
return e.getValue().toString();
}
});
 
return result + "]";
 
}
 
// *** static
 
/**
/trunk/OpenConcerto/src/org/openconcerto/sql/model/IFieldPath.java
32,6 → 32,8
*/
public SQLTable getTable();
 
public String getFieldName();
 
/**
* The field.
*
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/ToRefreshSpec.java
139,7 → 139,7
else
this.tablesFromJDBC = null;
} else {
tablesByRoot.addAll(tablesRefreshed);
tablesByRoot.merge(tablesRefreshed);
}
}
return this;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/DatabaseGraph.java
334,7 → 334,7
this.deleteGraphFiles();
}
// add to JDBC what wasn't loaded
fromJDBC.addAll(fromXML);
fromJDBC.merge(fromXML);
}
if (!fromJDBC.isEmpty()) {
final long t1 = System.currentTimeMillis();
/trunk/OpenConcerto/src/org/openconcerto/sql/model/graph/TablesMap.java
155,7 → 155,7
}
 
@Override
protected Set<String> createCollection(Collection<? extends String> v) {
public Set<String> createCollection(Collection<? extends String> v) {
final Set<String> res = new HashSet<String>(Math.max(8, v.size()));
res.addAll(v);
return res;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLFilter.java
54,9 → 54,9
final Set<SQLField> toKeep = new HashSet<SQLField>(elements.size());
 
for (final SQLElement elem : elements) {
final String parentFF = elem.getParentForeignField();
final SQLField parentFF = elem.getParentForeignField();
if (parentFF != null)
toKeep.add(elem.getTable().getField(parentFF));
toKeep.add(parentFF);
}
// NOTE, if filtering on privates is needed (eg OBSERVATION)
// first find its parents (eg CIRCUIT, MACHINE, etc.)
186,10 → 186,9
if (table == null)
connectedSet = this.filterGraph.getAllTables();
else {
// getFieldRaw since it can be inexistant
final String parentForeignField = this.getDirectory().getElement(table).getParentForeignField();
final SQLField parentForeignField = this.getDirectory().getElement(table).getParentForeignField();
// 5x faster than getElement(table).getDescendantTables()
connectedSet = this.filterGraph.getDescTables(table, table.getFieldRaw(parentForeignField));
connectedSet = this.filterGraph.getDescTables(table, parentForeignField);
}
 
final List<SQLFilterListener> dispatchingListeners;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/FieldPath.java
57,6 → 57,7
return this.p;
}
 
@Override
public final String getFieldName() {
return this.fieldName;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLDataSource.java
1390,6 → 1390,9
validateConnectionFactory(connectionFactory);
} catch (RuntimeException e) {
throw e;
} catch (SQLException e) {
// only wrap if necessary (calling code can use SQLState)
throw e;
} catch (Exception e) {
throw new SQLException("Cannot create PoolableConnectionFactory", e);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/correct/DeleteOrphans.java
46,9 → 46,9
return;
}
 
if (elem.getParentForeignField() != null) {
final SQLField parentF = elem.getParentForeignField();
if (parentF != null) {
final SQLTable elemT = elem.getTable();
final SQLField parentF = elemT.getField(elem.getParentForeignField());
final Where undefParent = new Where(parentF, "=", parentF.getForeignTable().getUndefinedID());
final Where undefW = new Where(elemT.getKey(), "!=", elemT.getUndefinedID());
getDS().execute("DELETE from " + elemT.getSQLName().quote() + " where " + undefParent.and(undefW));
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/correct/SetFFRules.java
64,7 → 64,7
 
getStream().println(t);
final AlterTable alterTable = new AlterTable(t);
final String parentFF = elem.getParentForeignField();
final String parentFF = elem.getParentForeignFieldName();
if (parentFF != null) {
setDeleteRule(t, parentFF, alterTable, Rule.CASCADE);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/correct/DeletePrivateOrphans.java
59,8 → 59,8
getStream().println(" : not a private table");
return;
}
if (elem.getParentForeignField() != null)
throw new IllegalStateException("Private with a parent : " + elem.getParentForeignField());
if (elem.getParentForeignFieldName() != null)
throw new IllegalStateException("Private with a parent : " + elem.getParentForeignFieldName());
final Set<SQLField> referentKeys = t.getDBSystemRoot().getGraph().getReferentKeys(t);
if (!referentKeys.equals(privateParentReferentFields))
throw new IllegalStateException("Table is not only private : " + referentKeys);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowValuesTable.java
19,6 → 19,7
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.request.MutableRowItemView;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.sql.sqlobject.itemview.BaseRowItemView;
import org.openconcerto.sql.view.IListPanel;
import org.openconcerto.ui.EnhancedTable;
import org.openconcerto.ui.state.JTableStateManager;
38,6 → 39,7
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
 
356,15 → 358,20
return this;
}
 
private SQLField field;
private List<SQLField> fields;
private String sqlName;
 
@Override
public SQLField getField() {
return this.field;
public final SQLField getField() {
return BaseRowItemView.getOnlyOne(this.fields);
}
 
@Override
public List<SQLField> getFields() {
return this.fields;
}
 
@Override
public String getSQLName() {
return this.sqlName;
}
437,11 → 444,10
 
@Override
public void init(String sqlName, Set<SQLField> fields) {
final Object[] array = fields.toArray();
if (array.length > 0) {
this.field = (SQLField) array[0];
if (fields.size() > 0) {
this.fields = new ArrayList<SQLField>(fields);
} else {
this.field = this.model.getRequiredField();
this.fields = Collections.singletonList(this.model.getRequiredField());
}
this.sqlName = sqlName;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelColumn.java
14,6 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.utils.cc.IClosure;
73,7 → 74,7
*/
public Set<SQLField> getFields() {
final Set<SQLField> res = new HashSet<SQLField>();
for (final FieldPath fp : this.getPaths())
for (final IFieldPath fp : this.getPaths())
res.add(fp.getField());
return res;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceOnline.java
13,7 → 13,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
import org.openconcerto.sql.request.ListSQLRequest;
48,7 → 48,7
super.colsChanged(change);
// add needed fields for each new column
for (final SQLTableModelColumn col : change.getItemsAdded()) {
for (final FieldPath p : col.getPaths()) {
for (final IFieldPath p : col.getPaths()) {
// don't back track : e.g. if path is SITE -> CLIENT <- SITE we want the siblings of
// SITE, if we want fields of the primary SITE we pass the path SITE
final SQLRowValues assurePath = this.getReq().getGraphToFetch().followPathToOne(p.getPath(), CreateMode.CREATE_ONE, false);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/listview/ListSQLView.java
219,10 → 219,15
 
public SQLField getField() {
// FIXME
Thread.dumpStack();
return null;
throw new UnsupportedOperationException();
}
 
@Override
public List<SQLField> getFields() {
// FIXME
throw new UnsupportedOperationException();
}
 
public void resetValue() {
this.getPool().reset();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/EditPanel.java
585,13 → 585,12
// eg /BATIMENT/
final SQLTable t = this.component.getElement().getTable();
final SQLRowValues vals = new SQLRowValues(t);
final String parentForeignField = this.component.getElement().getParentForeignField();
if (parentForeignField == null)
// eg |BATIMENT.ID_SITE|
final SQLField parentFF = this.component.getElement().getParentForeignField();
if (parentFF == null)
return;
// eg |BATIMENT.ID_SITE|
final SQLField parentFF = t.getField(parentForeignField);
// eg /SITE/
final SQLTable foreignT = t.getBase().getGraph().getForeignTable(parentFF);
final SQLTable foreignT = parentFF.getForeignTable();
 
for (int i = 0; i < sqlRows.size(); i++) {
final SQLRow row = sqlRows.get(i);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/TabbedListeModifyPanel.java
15,6 → 15,7
 
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.navigator.SQLBrowser;
import org.openconcerto.sql.view.list.IListe;
196,11 → 197,11
* @return
*/
private List getHiddenFields() {
final String parentField = element.getParentForeignField();
final SQLField parentField = this.element.getParentForeignField();
 
List hiddenFields = new Vector();
if (parentField != null)
hiddenFields.add(element.getTable().getField(parentField));
hiddenFields.add(parentField);
return hiddenFields;
}
 
/trunk/OpenConcerto/src/org/openconcerto/utils/ListMap.java
29,7 → 29,7
}
 
@Override
protected List<V> createCollection(Collection<? extends V> v) {
public List<V> createCollection(Collection<? extends V> v) {
return new ArrayList<V>(v);
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/CollectionMap2.java
66,7 → 66,7
 
private final boolean emptyCollSameAsNoColl;
private final Mode mode;
private Collection<V> allValues = null;
private transient Collection<V> allValues = null;
 
public CollectionMap2() {
this(DEFAULT_MODE);
382,12 → 382,18
}
}
 
public final void addAll(Map<? extends K, ? extends Collection<? extends V>> mm) {
public final void merge(Map<? extends K, ? extends Collection<? extends V>> mm) {
for (final Map.Entry<? extends K, ? extends Collection<? extends V>> e : mm.entrySet()) {
this.addAll(e.getKey(), e.getValue());
}
}
 
public final void mergeScalarMap(Map<? extends K, ? extends V> scalarMap) {
for (final Map.Entry<? extends K, ? extends V> e : scalarMap.entrySet()) {
this.add(e.getKey(), e.getValue());
}
}
 
public final void remove(K k, V v) {
this.removeAll(k, Collections.singleton(v));
}
441,6 → 447,14
}
}
 
public final void removeAllScalar(Map<? extends K, ? extends V> m) {
// incompatible types, allowing removal without ConcurrentModificationException
assert m != this;
for (final Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
this.remove(e.getKey(), e.getValue());
}
}
 
// ** remove empty/null collections
 
public final C removeIfEmpty(K k) {
478,7 → 492,7
return removed;
}
 
protected abstract C createCollection(Collection<? extends V> v);
public abstract C createCollection(Collection<? extends V> v);
 
@Override
public int hashCode() {
501,4 → 515,18
final CollectionMap2<?, ?, ?> other = (CollectionMap2<?, ?, ?>) obj;
return this.emptyCollSameAsNoColl == other.emptyCollSameAsNoColl && this.mode == other.mode;
}
 
@Override
public CollectionMap2<K, C, V> clone() {
@SuppressWarnings("unchecked")
final CollectionMap2<K, C, V> result = (CollectionMap2<K, C, V>) super.clone();
// allValues has a reference to this
result.allValues = null;
// clone each collection value
for (Map.Entry<K, C> entry : result.entrySet()) {
final C coll = entry.getValue();
entry.setValue(createCollection(coll));
}
return result;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/TimeUtils.java
19,16 → 19,18
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
 
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeConstants.Field;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.DatatypeConstants.Field;
 
import net.jcip.annotations.Immutable;
 
251,6 → 253,13
* {@link GregorianCalendar} then this method will use a GregorianCalendar with the time zone
* and absolute time of the other.
* </p>
* <p>
* Also note that the local time of <code>from</code> can be {@link #isAmbiguous(Calendar)
* ambiguous} in <code>to</code> or skipped. In the former case, the absolute time is
* unspecified (e.g. 2h30 in WET can either be 2h30 CEST or CET in fall). In the latter case, a
* round trip back to <code>from</code> will yield a different local time (e.g. in spring 2h30
* in WET to 3h30 in CEST, back to 3h30 in WEST).
* </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.
284,4 → 293,70
}
return to;
}
 
/**
* Whether the wall time of the passed calendar is ambiguous (i.e. happens twice the same day).
* E.g. in France, Sun Oct 27 02:30 CEST 2013 then one hour later Sun Oct 27 02:30 CET 2013.
*
* @param cal a calendar.
* @return <code>true</code> if the wall time (without time zone) is ambiguous.
*/
public final static boolean isAmbiguous(final Calendar cal) {
return isAmbiguous(getDSTChange(cal));
}
 
public final static boolean isAmbiguous(final DSTChange dstChange) {
return dstChange == DSTChange.BEFORE_WINTER || dstChange == DSTChange.AFTER_WINTER;
}
 
/**
* Relation to a DST change. Useful to test corner cases.
*
* @author Sylvain
*/
static public enum DSTChange {
/**
* No change nearby.
*/
NO,
/**
* DST will come, i.e. the next hour will be skipped.
*/
BEFORE_SUMMER,
/**
* DST just came, i.e. the previous hour was skipped.
*/
AFTER_SUMMER,
/**
* DST will go, the current hour will be happen again this day.
*/
BEFORE_WINTER,
/**
* DST just went, the current wall time has already happened this day.
*/
AFTER_WINTER
}
 
public final static DSTChange getDSTChange(final Calendar cal) {
final TimeZone tz = cal.getTimeZone();
if (!tz.useDaylightTime())
return DSTChange.NO;
 
final long currentMillis = cal.getTimeInMillis();
final int hourInMillis = tz.getDSTSavings();
final boolean isSTBefore = tz.inDaylightTime(new Date(currentMillis - hourInMillis));
final boolean isST = tz.inDaylightTime(new Date(currentMillis));
final boolean isSTAfter = tz.inDaylightTime(new Date(currentMillis + hourInMillis));
if (isSTBefore != isST) {
// only one change
assert isST == isSTAfter;
return isSTBefore ? DSTChange.AFTER_WINTER : DSTChange.AFTER_SUMMER;
} else if (isST != isSTAfter) {
// only one change
assert isST == isSTBefore;
return isSTAfter ? DSTChange.BEFORE_SUMMER : DSTChange.BEFORE_WINTER;
} else {
return DSTChange.NO;
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/SetMap.java
45,7 → 45,7
}
 
@Override
protected Set<V> createCollection(Collection<? extends V> v) {
public Set<V> createCollection(Collection<? extends V> v) {
return new HashSet<V>(v);
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/element/ComptePCESQLElement.java
86,10 → 86,10
final DBSystemRoot sysRoot = getTable().getDBSystemRoot();
final SQLTable ecrT = sysRoot.getGraph().findReferentTable(getTable(), "ECRITURE");
final UpdateBuilder updateBuilder = new UpdateBuilder(ecrT);
updateBuilder.addTable(getTable());
updateBuilder.set("COMPTE_NUMERO", getTable().getField("NUMERO").getFieldRef());
updateBuilder.set("COMPTE_NOM", getTable().getField("NOM").getFieldRef());
updateBuilder.setWhere(new Where(getTable().getKey(), "=", ecrT.getField("ID_COMPTE_PCE")).and(new Where(getTable().getKey(), "=", id)));
updateBuilder.addForwardVirtualJoin(getTable(), "ID_COMPTE_PCE");
updateBuilder.setFromVirtualJoinField("COMPTE_NUMERO", getTable().getAlias(), "NUMERO");
updateBuilder.setFromVirtualJoinField("COMPTE_NOM", getTable().getAlias(), "NOM");
updateBuilder.setWhere(new Where(ecrT.getField("ID_COMPTE_PCE"), "=", id));
sysRoot.getDataSource().execute(updateBuilder.asString());
}
 
/trunk/OpenConcerto/src/org/openconcerto/erp/core/common/ui/VilleRowItemView.java
14,10 → 14,8
package org.openconcerto.erp.core.common.ui;
 
import org.openconcerto.map.model.Ville;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.sqlobject.itemview.VWRowItemView;
import org.openconcerto.ui.valuewrapper.ValueWrapper;
 
27,24 → 25,19
super(wrapper);
}
 
public SQLField getField() {
return this.getFields().get(0);
}
 
protected final SQLTable getTable() {
return this.getField().getTable();
}
 
@Override
public void setEditable(boolean b) {
if (this.getComp() != null)
this.getComp().setEnabled(b);
}
 
@SuppressWarnings("unchecked")
@Override
public void show(SQLRowAccessor r) {
if (r.getFields().contains(this.getField().getName())) {
String cp = r.getString(getFields().get(0).getName());
String name = r.getString(getFields().get(1).getName());
final String fieldName0 = getFields().get(0).getName();
final String fieldName1 = getFields().get(1).getName();
if (r.getFields().contains(fieldName0) && r.getFields().contains(fieldName1)) {
String cp = r.getString(fieldName0);
String name = r.getString(fieldName1);
final Ville villeFromVilleEtCode = Ville.getVilleFromVilleEtCode(name + " (" + cp + ")");
// get a matching Ville
if (villeFromVilleEtCode != null) {
55,6 → 48,7
}
}
 
@Override
public void update(SQLRowValues vals) {
vals.put(getFields().get(1).getName(), this.isEmpty() ? SQLRowValues.SQL_DEFAULT : this.getWrapper().getValue().getName());
vals.put(getFields().get(0).getName(), this.isEmpty() ? SQLRowValues.SQL_DEFAULT : this.getWrapper().getValue().getCodepostal());
/trunk/OpenConcerto/src/org/openconcerto/erp/core/common/ui/AcompteRowItemView.java
13,10 → 13,8
package org.openconcerto.erp.core.common.ui;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.sqlobject.itemview.VWRowItemView;
import org.openconcerto.ui.valuewrapper.ValueWrapper;
 
28,24 → 26,19
super(wrapper);
}
 
public SQLField getField() {
return this.getFields().get(0);
}
 
protected final SQLTable getTable() {
return this.getField().getTable();
}
 
@Override
public void setEditable(boolean b) {
if (this.getComp() != null)
this.getComp().setEnabled(b);
}
 
@SuppressWarnings("unchecked")
@Override
public void show(SQLRowAccessor r) {
if (r.getFields().contains(this.getField().getName())) {
BigDecimal montant = r.getBigDecimal(getFields().get(0).getName());
BigDecimal percent = r.getBigDecimal(getFields().get(1).getName());
final String fieldName0 = getFields().get(0).getName();
final String fieldName1 = getFields().get(1).getName();
if (r.getFields().contains(fieldName0) && r.getFields().contains(fieldName1)) {
BigDecimal montant = r.getBigDecimal(fieldName0);
BigDecimal percent = r.getBigDecimal(fieldName1);
 
Acompte a = new Acompte(percent, montant);
this.getWrapper().setValue(a);
52,6 → 45,7
}
}
 
@Override
public void update(SQLRowValues vals) {
vals.put(getFields().get(0).getName(), this.isEmpty() ? SQLRowValues.SQL_DEFAULT : this.getWrapper().getValue().getMontant());
vals.put(getFields().get(1).getName(), this.isEmpty() ? SQLRowValues.SQL_DEFAULT : this.getWrapper().getValue().getPercent());
/trunk/OpenConcerto/src/org/openconcerto/erp/utils/correct/CorrectMouvement.java
72,6 → 72,7
final String selFixableUnbalanced = "( " + selUnbalanced.asString() + "\nEXCEPT\n" + selUnfixable.asString() + " )";
 
final UpdateBuilder updateUnbalanced = new UpdateBuilder(ecritureT);
// FIXME
updateUnbalanced.addRawTable(selFixableUnbalanced, SQLBase.quoteIdentifier("semiArchivedMvt"));
updateUnbalanced.setWhere(Where.quote("%i = %f", new SQLName("semiArchivedMvt", "ID_MOUVEMENT"), ecritureMvtFF));
updateUnbalanced.set(ecritureT.getArchiveField().getName(), "0");
105,6 → 106,7
selIdentifiableNonUsed.setHaving(Where.createRaw("count(*) = 1"));
 
final UpdateBuilder update = new UpdateBuilder(saisieKmElemT);
// FIXME
update.addTable(saisieKmT);
update.addRawTable("( " + selIdentifiableNonUsed.asString() + " )", "e");
 
/trunk/OpenConcerto/src/org/openconcerto/erp/config/ComptaPropsConfiguration.java
216,6 → 216,7
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
458,8 → 459,9
@Override
protected void initSystemRoot(DBSystemRoot input) {
super.initSystemRoot(input);
final JDialog f = new JOptionPane("Mise à jour des caches en cours...\nCette opération prend généralement moins d'une minute.", JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION,
null, new Object[] {}).createDialog("Veuillez patienter");
if (!GraphicsEnvironment.isHeadless()) {
final JDialog f = new JOptionPane("Mise à jour des caches en cours...\nCette opération prend généralement moins d'une minute.", JOptionPane.INFORMATION_MESSAGE,
JOptionPane.DEFAULT_OPTION, null, new Object[] {}).createDialog("Veuillez patienter");
input.addLoadingListener(new LoadingListener() {
 
private int loadingCount = 0;
504,6 → 506,7
}
});
}
}
 
@Override
protected void initDS(SQLDataSource ds) {
998,6 → 1001,11
 
@Override
protected SQLServer createServer() {
if (GraphicsEnvironment.isHeadless()) {
SQLServer server = super.createServer();
return server;
}
 
InProgressFrame progress = new InProgressFrame();
progress.show("Connexion à votre base de données en cours");
try {
/trunk/OpenConcerto/src/org/openconcerto/erp/config/InstallationPanel.java
25,7 → 25,6
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLField.Properties;
import org.openconcerto.sql.model.SQLInjector;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLRow;
38,10 → 37,11
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.SQLField.Properties;
import org.openconcerto.sql.model.graph.SQLKey;
import org.openconcerto.sql.request.Inserter;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.request.Inserter.Insertion;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.sqlobject.SQLTextCombo;
import org.openconcerto.sql.utils.AlterTable;
import org.openconcerto.sql.utils.ChangeTable;
587,20 → 587,21
dataSource.execute(alterEcheance.asString());
tableEch.getSchema().updateVersion();
tableEch.fetchFields();
if (root.getServer().getSQLSystem().equals(SQLSystem.POSTGRESQL)) {
 
// select MOUVEMENT whose parent has a source FACTURE
final SQLSelect selMvt = new SQLSelect();
final AliasedTable refChild = new AliasedTable(tableMvt, "m1");
final AliasedTable refParent = new AliasedTable(tableMvt, "m2");
selMvt.addSelect(refParent.getField("IDSOURCE"));
selMvt.addBackwardJoin("INNER", refChild.getField("ID_MOUVEMENT_PERE"), refParent.getAlias());
selMvt.addSelect(refChild.getKey());
selMvt.setWhere(new Where(refParent.getField("SOURCE"), "=", tableFacture.getName()));
 
final UpdateBuilder build = new UpdateBuilder(tableEch);
final AliasedTable refM1 = new AliasedTable(tableMvt, "m1");
final AliasedTable refM2 = new AliasedTable(tableMvt, "m2");
build.set("ID_SAISIE_VENTE_FACTURE", refM2.getField("IDSOURCE").getFieldRef());
build.addTable(refM1);
build.addTable(refM2);
Where w = new Where(refM1.getField("ID_MOUVEMENT_PERE"), "=", refM2.getKey());
w = w.and(new Where(refM1.getKey(), "=", build.getTable().getField("ID_MOUVEMENT")));
w = w.and(new Where(refM2.getField("SOURCE"), "=", tableFacture.getName()));
build.setWhere(w);
build.addVirtualJoin("( " + selMvt.asString() + " )", "mvt", false, tableMvt.getKey().getName(), "ID_MOUVEMENT");
build.setFromVirtualJoinField("ID_SAISIE_VENTE_FACTURE", "mvt", "IDSOURCE");
query = build.asString();
dataSource.execute(query);
}
} catch (SQLException ex) {
Log.get().severe("Error on query :" + query);
throw new IllegalStateException("Erreur lors de l'ajout des champs sur la table ECHEANCE_CLIENT", ex);
/trunk/OpenConcerto/src/org/openconcerto/erp/modules/ModuleTableModel.java
37,6 → 37,34
 
public class ModuleTableModel extends AbstractTableModel {
 
static public enum Problem {
/**
* The module is required but missing.
*/
REQUIRED_MISSING,
/**
* The module is installed but lacks at least one dependency.
*/
MISSING_DEP
}
 
// ref needed if factory is null
static private final boolean areDepsMet(final ModuleFactory factory, final InstallationState installationState) {
for (final Dependency dep : factory.getDependencies().values()) {
boolean depMet = false;
for (final String reqID : dep.getRequiredIDs()) {
final ModuleFactory f = installationState.getInstalledFactories().get(reqID);
if (f != null && dep.isRequiredFactoryOK(f)) {
depMet = true;
break;
}
}
if (!depMet)
return false;
}
return true;
}
 
@Immutable
static final class ModuleRow {
private final ModuleReference ref;
43,8 → 71,9
private final ModuleFactory factory;
private final boolean local, remote, registered, running;
private final boolean dbRequired, adminRequired;
private final Set<Problem> problems;
 
public ModuleRow(ModuleReference ref, ModuleFactory f, boolean local, boolean remote, boolean registered, boolean running, boolean dbRequired, boolean adminRequired) {
public ModuleRow(ModuleReference ref, ModuleFactory f, InstallationState installationState, boolean registered, boolean running, boolean dbRequired, boolean adminRequired) {
super();
if (ref == null)
throw new NullPointerException("Null reference");
51,13 → 80,24
this.ref = ref;
this.factory = f;
assert this.factory == null || this.factory.getReference().equals(this.ref);
this.local = local;
this.remote = remote;
this.local = installationState.getLocal().contains(ref);
this.remote = installationState.getRemote().contains(ref);
this.registered = registered;
this.running = running;
this.dbRequired = dbRequired;
this.adminRequired = adminRequired;
final Set<Problem> pbs = new HashSet<Problem>(Problem.values().length);
final boolean isInstalled = this.isInstalledLocally() || this.isInstalledRemotely();
if (!this.isAvailable() && isInstalled) {
pbs.add(Problem.REQUIRED_MISSING);
}
// if installed and not available, we cannot know its dependencies but it will be marked
// REQUIRED_MISSING
if (this.isAvailable() && isInstalled && !areDepsMet(this.factory, installationState)) {
pbs.add(Problem.MISSING_DEP);
}
this.problems = pbs.size() == 0 ? Collections.<Problem> emptySet() : Collections.unmodifiableSet(pbs);
}
 
public ModuleReference getRef() {
return this.ref;
95,6 → 135,10
return this.adminRequired;
}
 
public Set<Problem> getProblems() {
return this.problems;
}
 
@Override
public int hashCode() {
final int prime = 31;
143,8 → 187,6
public final void reload() throws IOException, SQLException {
final ModuleManager mngr = ModuleManager.getInstance();
final InstallationState installationState = new InstallationState(mngr);
final Collection<ModuleReference> remote = installationState.getRemote();
final Collection<ModuleReference> local = installationState.getLocal();
final Map<ModuleReference, ModuleFactory> available = new HashMap<ModuleReference, ModuleFactory>();
for (final Entry<String, SortedMap<ModuleVersion, ModuleFactory>> e : mngr.getFactories().entrySet()) {
for (final Entry<ModuleVersion, ModuleFactory> e2 : e.getValue().entrySet()) {
162,7 → 204,7
 
// if some installed modules lack their factory, we cannot know if they conflict with
// modules to install (see DepSolverResultMM.computeReferencesToRemove())
this.valid = installationState.getInstalledFactories() != null;
this.valid = true;
 
// all known references
final Set<ModuleReference> s = new HashSet<ModuleReference>(installationState.getLocalOrRemote());
169,8 → 211,9
s.addAll(available.keySet());
final List<ModuleRow> l = new ArrayList<ModuleRow>(s.size());
for (final ModuleReference ref : s) {
l.add(new ModuleRow(ref, available.get(ref), local.contains(ref), remote.contains(ref), registered.contains(ref), running.contains(ref), dbRequired.contains(ref), adminRequired
.contains(ref)));
final ModuleRow row = new ModuleRow(ref, available.get(ref), installationState, registered.contains(ref), running.contains(ref), dbRequired.contains(ref), adminRequired.contains(ref));
l.add(row);
this.valid &= row.getProblems().size() == 0;
}
 
// sort alphabetically and then highest version first
260,14 → 303,31
case VERSION:
return f.getRef().getVersion();
case STATE:
final String s;
if (f.isRunning())
return "Démarré";
s = "Démarré";
else if (f.isRegistered())
return "Chargé";
s = "Chargé";
else if (f.isAvailable())
return "Disponible";
s = "Disponible";
else
return "Non disponible";
s = "Non disponible";
final Set<Problem> pbs = f.getProblems();
if (pbs.size() == 0)
return s;
final StringBuilder sb = new StringBuilder(64);
sb.append("<html><body>");
sb.append(s);
// now add error tags
// space at the end for background
sb.append(" <font bgcolor=\"RED\" color=\"WHITE\">&nbsp;");
if (pbs.contains(Problem.REQUIRED_MISSING))
sb.append("Module&nbsp;requis ");
if (pbs.contains(Problem.MISSING_DEP))
sb.append("Dépendances&nbsp;manquantes ");
// leave last space for background
sb.append("</font></body></html>");
return sb.toString();
case LOCAL:
return f.isInstalledLocally();
case REMOTE:
/trunk/OpenConcerto/src/org/openconcerto/erp/modules/DepSolverResultMM.java
179,7 → 179,7
 
private final Set<ModuleReference> computeReferencesToRemove() {
final Set<ModuleFactory> factories = this.getGraph().getFactories();
final Set<ModuleFactory> installedFactories = this.getInstallState().getInstalledFactories();
final Collection<ModuleFactory> installedFactories = this.getInstallState().getAllInstalledFactories();
// missing some factories for installed modules
if (installedFactories == null)
return null;
/trunk/OpenConcerto/src/org/openconcerto/erp/modules/ModulePanel.java
82,6 → 82,8
return viewPort.getSize().width >= this.getMinimumSize().width;
}
};
// perhaps pass custom sorter to be able to unsort
t.setAutoCreateRowSorter(true);
t.setShowGrid(false);
t.setShowVerticalLines(false);
t.setFocusable(false);
111,7 → 113,7
col.setMinWidth(minCellWidth);
final int prefCellWidth;
if (col.getIdentifier() == ModuleTableModel.Columns.NAME || col.getIdentifier() == ModuleTableModel.Columns.STATE)
prefCellWidth = 128;
prefCellWidth = 192;
else
prefCellWidth = minCellWidth;
// makes sure the column can display its label
168,7 → 170,7
c.fill = GridBagConstraints.NONE;
c.gridheight = 1;
 
final JButton installButton = new JButton(AvailableModulesPanel.createInstallAction(this, this.tm, onlyInstall));
final JButton installButton = new JButton(AvailableModulesPanel.createInstallAction(this, onlyInstall));
installButton.setOpaque(false);
this.add(installButton, c);
 
187,7 → 189,7
@Override
public void actionPerformed(ActionEvent evt) {
final String dialogTitle = "Désinstallation de modules";
final Collection<ModuleRow> checkedRows = ModulePanel.this.tm.getCheckedRows();
final Collection<ModuleRow> checkedRows = getSelection();
if (checkedRows.isEmpty()) {
JOptionPane.showMessageDialog(ModulePanel.this, "Aucune ligne cochée", dialogTitle, JOptionPane.INFORMATION_MESSAGE);
return;
292,6 → 294,12
}
}
 
protected final Collection<ModuleRow> getSelection() {
// don't use native selection since users have some difficulty to select multiple rows
// NOTE: since we use a RowSorter this also frees us from converting indexes
return this.tm.getCheckedRows();
}
 
// this doesn't change the installation state, only start/stop
private final class StartStopAction extends AbstractAction {
private final boolean start;
311,7 → 319,7
public void actionPerformed(ActionEvent evt) {
final ModuleManager mngr = ModuleManager.getInstance();
final String dialogTitle = this.start ? "Démarrage de modules" : "Arrêt de modules";
final Collection<ModuleRow> checkedRows = ModulePanel.this.tm.getCheckedRows();
final Collection<ModuleRow> checkedRows = getSelection();
if (checkedRows.isEmpty()) {
JOptionPane.showMessageDialog(ModulePanel.this, "Aucune ligne cochée", dialogTitle, JOptionPane.INFORMATION_MESSAGE);
return;
/trunk/OpenConcerto/src/org/openconcerto/erp/modules/ModuleManager.java
1702,9 → 1702,11
final ListIterator<Locale> listIterator = CollectionUtils.getListIterator(langs, true);
while (listIterator.hasNext()) {
final Locale lang = listIterator.next();
final InputStream ins = module.getClass().getResourceAsStream(cntrl.toResourceName(cntrl.toBundleName(baseName, lang), "xml"));
final String resourceName = cntrl.toResourceName(cntrl.toBundleName(baseName, lang), "xml");
final InputStream ins = module.getClass().getResourceAsStream(resourceName);
// do not force to have one mapping for each locale
if (ins != null) {
Log.get().info("module " + module.getName() + " loading translation from " + resourceName);
final Set<SQLTable> loadedTables;
try {
loadedTables = trns.load(getRoot(), mdVariant, ins).get0();
/trunk/OpenConcerto/src/org/openconcerto/erp/modules/AvailableModulesPanel.java
151,12 → 151,12
}
}
 
static final Action createInstallAction(final ModulePanel panel, final ModuleTableModel tm, final boolean onlyInstall) {
static final Action createInstallAction(final ModulePanel panel, final boolean onlyInstall) {
return new AbstractAction("Installer") {
@Override
public void actionPerformed(ActionEvent evt) {
final String dialogTitle = "Installation de modules";
final Collection<ModuleRow> checkedRows = tm.getCheckedRows();
final Collection<ModuleRow> checkedRows = panel.getSelection();
if (checkedRows.isEmpty()) {
JOptionPane.showMessageDialog(panel, "Aucune ligne cochée", dialogTitle, JOptionPane.INFORMATION_MESSAGE);
return;
/trunk/OpenConcerto/src/org/openconcerto/erp/modules/InstallationState.java
15,20 → 15,27
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.Tuple2;
 
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
import net.jcip.annotations.Immutable;
 
/**
* Stores in-memory which modules are installed (avoiding file system and database access).
*
* @author Sylvain
*/
@Immutable
final class InstallationState {
 
static private <T> Set<T> copySet(final Collection<T> s) {
41,7 → 48,8
private final Set<ModuleReference> installedRemotely;
private final Set<ModuleReference> localAndRemote;
private final Set<ModuleReference> localOrRemote;
private final Set<ModuleFactory> installedFactories;
private final boolean missingFactories;
private final Map<String, ModuleFactory> installedFactories;
 
private InstallationState() {
this(Collections.<ModuleReference> emptySet(), Collections.<ModuleReference> emptySet(), null);
63,7 → 71,9
tmp.addAll(this.installedRemotely);
this.localOrRemote = Collections.unmodifiableSet(tmp);
 
this.installedFactories = this.computeInstalledFactories(pool);
final Tuple2<Boolean, Map<String, ModuleFactory>> computed = this.computeInstalledFactories(pool);
this.missingFactories = computed.get0();
this.installedFactories = computed.get1();
}
 
public Set<ModuleReference> getLocal() {
82,25 → 92,42
return this.localOrRemote;
}
 
// true if some installed module lacks a factory
public final boolean isMissingFactories() {
return this.missingFactories;
}
 
// available factories for installed modules
public final Map<String, ModuleFactory> getInstalledFactories() {
return this.installedFactories;
}
 
// factories for all installed (local or remote) modules
// null if some installed module lacks a factory
public final Set<ModuleFactory> getInstalledFactories() {
return this.installedFactories;
public final Collection<ModuleFactory> getAllInstalledFactories() {
if (this.isMissingFactories())
return null;
else
return this.getInstalledFactories().values();
}
 
private final Set<ModuleFactory> computeInstalledFactories(final FactoriesByID pool) {
private final Tuple2<Boolean, Map<String, ModuleFactory>> computeInstalledFactories(final FactoriesByID pool) {
boolean missing = false;
final Set<ModuleReference> localOrRemote = this.getLocalOrRemote();
final Set<ModuleFactory> res = new HashSet<ModuleFactory>(localOrRemote.size());
final Map<String, ModuleFactory> res = new HashMap<String, ModuleFactory>(localOrRemote.size());
for (final ModuleReference ref : localOrRemote) {
if (ref.getVersion() == null)
throw new IllegalStateException("Installed module missing version : " + ref);
final ModuleFactory factory = CollectionUtils.getSole(pool.getFactories(ref));
if (factory == null)
return null;
res.add(factory);
final List<ModuleFactory> factories = pool.getFactories(ref);
if (factories.size() == 0) {
missing = true;
} else {
assert factories.size() == 1 : "Despite a non-null version, more than one match";
res.put(ref.getID(), CollectionUtils.getSole(factories));
}
return Collections.unmodifiableSet(res);
}
return Tuple2.create(missing, Collections.unmodifiableMap(res));
}
 
@Override
public int hashCode() {