OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 144 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.task;

import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.users.User;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.task.config.ComptaBasePropsConfiguration;
import org.openconcerto.utils.cc.IFactory;

import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;

public class TodoListModel extends AbstractTableModel {

    public static final int EXTENDED_MODE = 1;
    public static final int SIMPLE_MODE = 2;
    public int mode = SIMPLE_MODE;
    private static final int MIN_DELAY = 30;// * 1000; // en secondes
    private static final int MAX_DELAY = 6;// 120 * 1000;
    private long currentDelay = MIN_DELAY;
    private boolean stop = false;
    private final List<Integer> listIdListener = new Vector<Integer>(); // Contient des Integer, id
    // que l'on ecoute
    private JTable table = null;
    private List<ModelStateListener> stateListenerList = new Vector<ModelStateListener>(1);
    private final UserManager uMngr;
    protected List<UserTaskRight> rights;
    private boolean historyVisible = false;
    private List<TodoListElement> elements = new ArrayList<TodoListElement>();

    TodoListModel(final UserManager uMngr) {
        this.uMngr = uMngr;
        launchUpdaterThread();
        this.mode = SIMPLE_MODE;
    }

    private void launchUpdaterThread() {
        final Thread thread = new Thread(new Runnable() {

            public void run() {
                // Remplissage périodique
                while (!TodoListModel.this.stop) {
                    try {
                        Thread.sleep(TodoListModel.this.currentDelay * 1000);
                        if (!TodoListModel.this.stop) {
                            synchronousFill();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // this only read data to be displayed, it can be safely interrupted at any moment
        thread.setDaemon(true);
        thread.setName("TodoListModel UpdaterThread");
        thread.start();
    }

    public void asynchronousFill() {
        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronousFill();
            }
        });
        thread.setName("TodoListModel asynchronousFill");
        thread.start();
    }

    /**
     * 
     */
    private synchronized void synchronousFill() {
        if (this.table == null)
            return;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                fireModelStateChanged(ModelStateListener.STATE_RELOADING);
            }
        });

        final Map<Integer, TodoListElement> newDataVector = new LinkedHashMap<Integer, TodoListElement>();
        try {
            fillFromDatabase(newDataVector);

        } catch (Exception e) {
            e.printStackTrace();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    fireModelStateChanged(ModelStateListener.STATE_DEAD);
                }
            });
            return;
        }

        final Vector<Integer> rowsModified = new Vector<Integer>();
        final Vector<TodoListElement> rowsDeleted = new Vector<TodoListElement>();
        // size before removing
        final int newSize = newDataVector.size();
        final int oldSize;
        synchronized (this.elements) {
            oldSize = this.elements.size();
            for (int i = 0; i < oldSize; i++) {
                final TodoListElement elt = this.elements.get(i);
                final TodoListElement eltN = newDataVector.remove(elt.getRowValues().getID());
                if (eltN == null) {
                    rowsDeleted.add(elt);
                } else {
                    if (!eltN.equals(elt)) {
                        rowsModified.add(i);
                        elt.reloadValues(eltN.getRowValues());
                    }
                }
            }

            for (TodoListElement elt : rowsDeleted) {
                int index = this.elements.indexOf(elt);
                if (index >= 0) {
                    elements.remove(index);
                }
            }

            for (Integer i : newDataVector.keySet()) {
                this.elements.add(newDataVector.get(i));
            }
        }
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                if ((rowsModified.size() == newSize) && !rowsModified.isEmpty()) {
                    fireTableDataChanged();
                    fireModelStateChanged(ModelStateListener.CONTENT_MODIFIED);
                } else if (newSize != oldSize) {
                    fireTableDataChanged();
                    fireModelStateChanged(ModelStateListener.CONTENT_MODIFIED);
                } else {
                    for (int i = 0; i < rowsModified.size(); i++) {
                        Integer indexModified = rowsModified.get(i);
                        fireTableRowsUpdated(indexModified, indexModified);
                    }
                    if (!rowsModified.isEmpty()) {
                        fireModelStateChanged(ModelStateListener.CONTENT_MODIFIED);
                    }
                }
                fireModelStateChanged(ModelStateListener.STATE_OK);
            }
        });

    }

    private final Where getAuthorizedTaskTypes(final int userID, final SQLTable tableTache) {
        final SQLField typeF = tableTache.getFieldRaw("TYPE");
        if (typeF == null)
            return null;

        final Set<String> types = UserRightsManager.getInstance().getObjects(userID, "TASK", new IFactory<Set<String>>() {
            @SuppressWarnings("unchecked")
            @Override
            public Set<String> createChecked() {
                final SQLSelect sel = new SQLSelect();
                sel.addSelect(typeF);
                sel.addGroupBy(typeF);
                return new HashSet<String>(tableTache.getDBSystemRoot().getDataSource().executeCol(sel.asString()));
            }
        });
        return types == null ? Where.TRUE : new Where(typeF, types);
    }

    private static final int societeID = ((ComptaBasePropsConfiguration) Configuration.getInstance()).getSocieteID();
    private static final DBSystemRoot base = Configuration.getInstance().getSystemRoot();
    private static final SQLTable tableTache = base.getRoot("Common").getTable("TACHE_COMMON");
    private static final Where where2 = new Where(tableTache.getField("ID_SOCIETE_COMMON"), "=", tableTache.getUndefinedID()).or(new Where(tableTache.getField("ID_SOCIETE_COMMON"), "=", societeID));

    private synchronized void fillFromDatabase(final Map<Integer, TodoListElement> m) {
        long time1 = System.currentTimeMillis();

        final SQLSelect select = new SQLSelect();
        select.addSelectStar(tableTache);
        Where where = new Where(tableTache.getField("ID_USER_COMMON_TO"), this.listIdListener);
        final int userID = getCurrentUser().getId();
        where = where.or(new Where(tableTache.getField("ID_USER_COMMON_ASSIGN_BY"), "=", userID));

        where = where.or(getAuthorizedTaskTypes(userID, tableTache));

        if (!isHistoryVisible()) {
            Where w3 = new Where(tableTache.getField("FAIT"), "=", Boolean.FALSE);
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            Where w4 = new Where(tableTache.getField("DATE_FAIT"), "<>", (Object) null);
            w4 = w4.and(new Where(tableTache.getField("DATE_FAIT"), ">", cal.getTime()));
            w3 = w3.or(w4);
            where = where.and(w3);
        }
        select.setWhere(where.and(where2));
        select.addFieldOrder(tableTache.getField("ID_USER_COMMON_TO"));
        select.addFieldOrder(tableTache.getField("DATE_EXP"));

        this.rights = UserTaskRight.getUserTaskRight(getCurrentUser());
        // don't use the cache since by definition this table is shared by everyone, so we can't
        // rely on our modifications
        final List<SQLRow> l = SQLRowListRSH.execute(select, false, false);
        for (SQLRow row : l) {
            final TodoListElement t = new TodoListElement(getUserManager(), row.asRowValues());
            // add tasks that we created, we must do, or that we can read
            // plus for preventec, tasks with a type must visible to everybody
            boolean add = false;
            String type = row.getString("TYPE");
            if (type != null && type.trim().length() > 0) {
                add = true;
            } else if (row.getInt("ID_USER_COMMON_CREATE") == userID || row.getInt("ID_USER_COMMON_TO") == userID) {
                add = true;
            } else {
                for (int i = 0; i < TodoListModel.this.rights.size(); i++) {
                    final UserTaskRight element = TodoListModel.this.rights.get(i);
                    if (element.getIdToUser() == row.getInt("ID_USER_COMMON_TO") && element.canRead()) {
                        add = true;
                        break;
                    }
                }
            }
            if (add)
                m.put(row.getID(), t);
        }

        long time2 = System.currentTimeMillis();
        final long t = time2 - time1;
        // System.err.println("Time to fill from DB : " + t);
        long delay = 2 + t / 1000;
        if (delay > MAX_DELAY)
            delay = MAX_DELAY;
        if (delay < MIN_DELAY)
            delay = MIN_DELAY;
        this.currentDelay = delay;
    }

    public int getColumnCount() {
        if (this.mode == EXTENDED_MODE)
            return 7;
        return 5;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        synchronized (this.elements) {
            // Modifier la priorité de la tache
            if (columnIndex == 1) {
                return true;
            }
            if (this.mode == EXTENDED_MODE && columnIndex == 3) {
                // Impossible de modifier la date de creation
                return false;
            }

            if (this.elements.size() <= rowIndex) {
                System.err.println("Size error :" + this.elements.size() + " i:" + rowIndex);
                rowIndex = 0;
            }
            final TodoListElement task = this.elements.get(rowIndex);
            if (task == null)
                return false;
            final int size = this.rights.size();
            if (columnIndex == 0)
                // Validation
                for (int i = 0; i < size; i++) {
                    UserTaskRight right = this.rights.get(i);
                    if (right.getIdToUser() == task.getUserId() && right.canValidate()) {
                        return true;
                    }
                }
            else if (columnIndex == this.getColumnCount() - 1) {
                // Colonne d'assignement
                return (task.getCreatorId().equals(getCurrentUser().getId()));
            } else {
                // Modification
                for (int i = 0; i < size; i++) {
                    UserTaskRight right = this.rights.get(i);
                    // i.e. we can still modify tasks we created and assigned to another user, but
                    // we cannot change tasks assigned to us
                    if (right.getIdToUser() == task.getCreatorId() && right.canModify()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public Class<?> getColumnClass(final int columnIndex) {
        switch (columnIndex) {
        case 0:
            return Boolean.class;
        case 1:
            return Integer.class;
        case 2:
            return String.class;
        case 3:
            return Timestamp.class;
        case 4:
            if (this.mode == EXTENDED_MODE) {
                return Timestamp.class;
            }
            return Integer.class;
        case 5:
            return Timestamp.class;
        case 6:
            return Integer.class;
        default:
            return String.class;
        }
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        synchronized (this.elements) {
            if (this.elements.size() <= rowIndex) {
                System.err.println("Size error :" + this.elements.size() + " i:" + rowIndex);
                rowIndex = 0;
            }
            final TodoListElement task = this.elements.get(rowIndex);
            switch (columnIndex) {
            case 0:
                return task.isDone();
            case 1:
                return task.getPriority();
            case 2:
                return task.getName();
            case 3:
                if (this.mode == EXTENDED_MODE) {
                    return task.getDate();
                }
                return task.getExpectedDate();
            case 4:
                if (this.mode == EXTENDED_MODE) {
                    return task.getDoneDate();
                }
                return task.getUserId();
            case 5:
                return task.getExpectedDate();
            case 6:
                return task.getUserId();
            default:
                return "????????";
            }
        }
    }

    public TodoListElement getTaskAtRow(int rowIndex) {
        return this.elements.get(rowIndex);
    }

    @Override
    public int getRowCount() {
        return this.elements.size();
    }

    public void removeRow(int row) {
        elements.remove(row);
        fireTableRowsDeleted(row, row);
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        synchronized (this.elements) {
            if (rowIndex >= getRowCount()) {
                // Cas de la perte de l'edition de la derniere ligne supprimee
                return;
            }
            final TodoListElement task = this.elements.get(rowIndex);
            switch (columnIndex) {
            case 0:
                task.setDone((Boolean) aValue);
                break;
            case 1:
                task.setPriority((Integer) aValue);
                break;
            case 2:
                task.setName((String) aValue);
                break;
            case 3:
                if (this.mode == EXTENDED_MODE) {
                    task.setDate((Timestamp) aValue);
                }
                task.setExpectedDate((Timestamp) aValue);
                break;
            case 4:
                if (this.mode == EXTENDED_MODE) {
                    task.setDoneDate((Timestamp) aValue);
                    break;
                }
                task.setUserId((Integer) aValue);
                break;
            case 5:
                task.setExpectedDate((Timestamp) aValue);
                break;
            case 6:
                task.setUserId((Integer) aValue);
                break;
            default:
                break;
            }
            task.commitChanges();
        }
        fireTableRowsUpdated(rowIndex, rowIndex);
    }

    @Override
    public String getColumnName(int columnIndex) {
        switch (columnIndex) {
        case 0:
            return "";
        case 1:
            return "";
        case 2:
            return TM.tr("taskToDo");
        case 3:
            if (this.mode == EXTENDED_MODE) {
                return TM.tr("created");
            }
            return TM.tr("todoBefore.col");
        case 4:
            if (this.mode == EXTENDED_MODE) {
                return TM.tr("completed");
            }
            return TM.tr("assignedTo");
        case 5:
            return TM.tr("todoBefore.col");
        case 6:
            return TM.tr("assignedTo");
        default:
            return "?????";

        }
    }

    /**
     * Ajoute une nouvelle Tâche de manière asynchrone
     */
    public void addNewTask() {
        final SwingWorker<?, ?> worker = new SwingWorker<Object, Object>() {

            @Override
            public Object doInBackground() {
                final SQLRowValues rowV = new SQLRowValues(tableTache);
                final Calendar cal = Calendar.getInstance();
                rowV.put("DATE_ENTREE", new java.sql.Timestamp(cal.getTimeInMillis()));
                cal.add(Calendar.HOUR_OF_DAY, 1);
                rowV.put("DATE_EXP", new java.sql.Timestamp(cal.getTimeInMillis()));
                cal.set(Calendar.YEAR, 2000);
                cal.set(Calendar.DAY_OF_YEAR, 1);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.MILLISECOND, 0);
                rowV.put("DATE_FAIT", new java.sql.Timestamp(cal.getTimeInMillis()));
                final int currentUserId = getCurrentUser().getId();
                rowV.put("ID_USER_COMMON_ASSIGN_BY", currentUserId);
                rowV.put("ID_USER_COMMON_TO", currentUserId);
                try {
                    rowV.insert();
                } catch (SQLException e) {
                    fireModelStateChanged(ModelStateListener.STATE_DEAD);
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public void done() {
                // synchrone pour que le fire fonctionne bien
                synchronousFill();
                fireTableRowsInserted(getRowCount(), getRowCount());
            }

        };
        worker.execute();

    }

    synchronized void setMode(int mode) {
        this.mode = mode;
        fireTableStructureChanged();
    }

    public synchronized int getMode() {
        return this.mode;
    }

    public boolean deleteTaskAtIndex(int index) {
        synchronized (this.elements) {
            final TodoListElement t = this.elements.get(index);
            final int currentUserId = getCurrentUser().getId();
            if (t.getCreatorId() != currentUserId) {
                JOptionPane.showMessageDialog(this.table, TM.tr("deleteForbidden"));
                return false;
            }
            t.archive();
        }
        removeRow(index);
        return true;
    }

    public void addIdListener(Integer id) {
        this.listIdListener.add(id);
        asynchronousFill();
    }

    public void addIdListenerSilently(Integer id) {
        this.listIdListener.add(id);
    }

    public void removeIdListener(Integer id) {
        this.listIdListener.remove(id);
        asynchronousFill();
    }

    public boolean listenToId(Integer id) {
        return this.listIdListener.contains(id);
    }

    public void setTable(JTable t) {
        this.table = t;
    }

    public void stopUpdate() {
        this.stop = true;
    }

    public void addModelStateListener(ModelStateListener l) {
        if (!this.stateListenerList.contains(l)) {
            this.stateListenerList.add(l);
        }
    }

    public void removeModelStateListener(ModelStateListener l) {
        if (this.stateListenerList.contains(l)) {
            this.stateListenerList.remove(l);
        }
    }

    private void fireModelStateChanged(int state) {
        final int size = this.stateListenerList.size();
        for (int i = 0; i < size; i++) {
            this.stateListenerList.get(i).stateChanged(state);
        }
    }

    public final UserManager getUserManager() {
        return this.uMngr;
    }

    public final User getCurrentUser() {
        return this.getUserManager().getCurrentUser();
    }

    public synchronized boolean isHistoryVisible() {
        return this.historyVisible;
    }

    public synchronized void setHistoryVisible(boolean historyVisible) {
        this.historyVisible = historyVisible;
    }

}