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



Rev 151 | Blame | Compare with Previous | Last modification | View Log | RSS feed

 * Copyright 2011-2019 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 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.sql.model.SQLRowValues;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.utils.Value;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Future;

// use SQLRowValues to allow graph
public abstract class SQLTableModelLinesSource {

    private final ITableModel model;
    private final PropertyChangeListener reqListener;
    private final List<PropertyChangeListener> listeners;
    private IPredicate<SQLRowValues> filter;

        this.listeners = new ArrayList<PropertyChangeListener>();
        this.filter = null;

    protected SQLTableModelLinesSource(final ITableModel model) {
        this.model = model;
        this.reqListener = new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {

    void live() {

    void die() {

    public final ITableModel getModel() {
        return this.model;

    public abstract SQLTableModelSource getParent();

    public final ListSQLRequest getUpdateQueueReq() {
        return this.getModel().getUpdateQ().getState().getReq();

    public final List<ListSQLLine> getAll() {
        return this.get(null);

     * Fetch up to date values from the DB.
     * @param ids which rows to fetch, <code>null</code> meaning all.
     * @return the new values from the DB, some changes in the DB might be ignored if there's
     *         pending changes in this.
    public abstract List<ListSQLLine> get(final Collection<? extends Number> ids);

     * A row in the DB has been changed, fetch its current value.
     * @param id a valid ID of a database row.
     * @return if not {@link Value#hasValue()} the event should be ignored, otherwise the new value
     *         for the passed ID, <code>null</code> if it is not part of this.
    public abstract Value<ListSQLLine> get(final int id);

     * Implementations should only use state of the parameters, so that this method can be
     * thread-safe (it is called both by the {@link UpdateQueue} and by the EDT in
     * {@link ITableModel}). This implementation order lines like the source {@link ListSQLRequest
     * request}.
     * @param l1 the first line.
     * @param l2 the second line.
     * @return a negative integer, zero, or a positive integer as this object is less than, equal
     *         to, or greater than the specified object.
     * @see Comparator#compare(Object, Object)
    public int compare(ListSQLLine l1, ListSQLLine l2) {
        return ListSQLLine.compareLikeRequest(l1, l2);

    public boolean isCellEditable(ListSQLLine line, int colIndex, SQLTableModelColumn col) {
        return true;

    public abstract Future<?> moveBy(final List<? extends SQLRowAccessor> rows, final int inc);

    // take IDs :
    // 1. no need to protect rows from modifications, just copy IDs
    // 2. for non committed rows, i.e. without DB ID and thus without SQLRow, it's easier to handle
    // (virtual) IDs than to use IdentitySet of SQLRowValues
    public abstract Future<?> moveTo(final List<? extends Number> rows, final int rowIndex);

    public final void setFilter(final IPredicate<SQLRowValues> filter) {
        // always fire since for now there's no other way for the caller
        // (ie if the meaning of filter change, it has to do setFilter(getFilter()) )
        this.filter = filter;
        this.fireChanged(new PropertyChangeEvent(this, "filter", null, this.filter));

    public final IPredicate<SQLRowValues> getFilter() {
        return this.filter;

     * Adds a listener to be notified when {@link #getAll()} change value.
     * @param l the listener.
    public final void addListener(PropertyChangeListener l) {

    public final void rmListener(PropertyChangeListener l) {

    protected final void fireChanged(PropertyChangeEvent evt) {
        for (final PropertyChangeListener l : this.listeners)

    protected final ListSQLLine createLine(final SQLRowValues v) {
        return this.createLine(v, null);

     * Create a line with the passed row.
     * @param v the values.
     * @param passedID the {@link ListSQLLine#getID() ID} of the result, <code>null</code> meaning
     *        {@link SQLRowValues#getID()}.
     * @return a new line.
     * @throws IllegalArgumentException if <code>passedID</code> is <code>null</code> and
     *         <code>v</code> {@link SQLRowValues#hasID() has no ID}.
    protected final ListSQLLine createLine(final SQLRowValues v, final Number passedID) {
        if (v == null || (this.filter != null && !this.filter.evaluateChecked(v)))
            return null;
        final int id;
        if (passedID != null) {
            id = passedID.intValue();
        } else if (v.hasID()) {
            id = v.getID();
        } else {
            throw new IllegalArgumentException("No ID for " + v);
        final ListSQLLine res = new ListSQLLine(this, v, id, this.getModel().getUpdateQ().getState());
        return res;

     * A new line has been created. This implementation does nothing.
     * @param res the newly created line.
    protected void lineCreated(ListSQLLine res) {

    final void colsChanged(final SQLTableModelSourceState beforeState, final SQLTableModelSourceState afterState) {
        this.model.getUpdateQ().stateChanged(beforeState, afterState);

     * Change the line <code>l</code> at the passed path with the passed values.
     * @param l the line to change, eg RECEPTEUR[12].
     * @param path the changing path, eg RECEPTEUR.ID_LIMITEUR.
     * @param vals the new values, eg LIMITEUR{ID=4, DESIGNATION="dess"}.
     * @throws SQLException if the values cannot be commited.
    public abstract void commit(ListSQLLine l, Path path, SQLRowValues vals) throws SQLException;